gooddata 0.6.11 → 0.6.12
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +6 -0
- data/.travis.yml +5 -0
- data/CHANGELOG.md +34 -1
- data/CLI.md +1 -1
- data/authors.sh +4 -0
- data/lib/gooddata.rb +1 -1
- data/lib/gooddata/cli/commands/api_cmd.rb +0 -2
- data/lib/gooddata/cli/commands/auth_cmd.rb +0 -3
- data/lib/gooddata/cli/commands/console_cmd.rb +1 -2
- data/lib/gooddata/cli/commands/domain_cmd.rb +0 -2
- data/lib/gooddata/cli/commands/process_cmd.rb +0 -2
- data/lib/gooddata/cli/commands/project_cmd.rb +0 -2
- data/lib/gooddata/cli/commands/projects_cmd.rb +0 -2
- data/lib/gooddata/cli/commands/run_ruby_cmd.rb +2 -3
- data/lib/gooddata/cli/commands/scaffold_cmd.rb +0 -3
- data/lib/gooddata/cli/commands/user_cmd.rb +0 -2
- data/lib/gooddata/cli/shared.rb +1 -2
- data/lib/gooddata/commands/datawarehouse.rb +24 -0
- data/lib/gooddata/commands/process.rb +0 -1
- data/lib/gooddata/commands/project.rb +1 -1
- data/lib/gooddata/commands/scaffold.rb +0 -1
- data/lib/gooddata/core/connection.rb +376 -0
- data/lib/gooddata/core/logging.rb +13 -0
- data/lib/gooddata/core/rest.rb +40 -16
- data/lib/gooddata/exceptions/user_in_different_domain.rb +11 -0
- data/lib/gooddata/extensions/enumerable.rb +8 -0
- data/lib/gooddata/goodzilla/goodzilla.rb +24 -0
- data/lib/gooddata/helpers/global_helpers.rb +126 -12
- data/lib/gooddata/mixins/author.rb +11 -5
- data/lib/gooddata/mixins/is_dimension.rb +13 -0
- data/lib/gooddata/mixins/md_object_indexer.rb +17 -1
- data/lib/gooddata/mixins/md_object_query.rb +10 -2
- data/lib/gooddata/mixins/md_relations.rb +2 -2
- data/lib/gooddata/mixins/rest_resource.rb +1 -0
- data/lib/gooddata/models/data_result.rb +0 -1
- data/lib/gooddata/models/datawarehouse.rb +90 -0
- data/lib/gooddata/models/domain.rb +202 -76
- data/lib/gooddata/models/execution.rb +11 -0
- data/lib/gooddata/models/from_wire.rb +4 -4
- data/lib/gooddata/models/invitation.rb +0 -5
- data/lib/gooddata/models/membership.rb +121 -91
- data/lib/gooddata/models/metadata.rb +1 -2
- data/lib/gooddata/models/metadata/attribute.rb +7 -0
- data/lib/gooddata/models/metadata/dashboard.rb +1 -1
- data/lib/gooddata/models/metadata/dimension.rb +52 -0
- data/lib/gooddata/models/metadata/fact.rb +1 -1
- data/lib/gooddata/models/metadata/label.rb +21 -7
- data/lib/gooddata/models/metadata/metric.rb +1 -23
- data/lib/gooddata/models/metadata/report.rb +2 -2
- data/lib/gooddata/models/metadata/report_definition.rb +22 -2
- data/lib/gooddata/models/metadata/variable.rb +81 -0
- data/lib/gooddata/models/model.rb +2 -1
- data/lib/gooddata/models/process.rb +3 -4
- data/lib/gooddata/models/profile.rb +50 -82
- data/lib/gooddata/models/project.rb +170 -213
- data/lib/gooddata/models/project_blueprint.rb +14 -5
- data/lib/gooddata/models/project_creator.rb +2 -2
- data/lib/gooddata/models/schedule.rb +10 -8
- data/lib/gooddata/models/to_wire.rb +2 -2
- data/lib/gooddata/models/user_filters/mandatory_user_filter.rb +67 -0
- data/lib/gooddata/models/user_filters/user_filter.rb +96 -0
- data/lib/gooddata/models/user_filters/user_filter_builder.rb +409 -0
- data/lib/gooddata/{rest/connections/connections.rb → models/user_filters/user_filters.rb} +1 -0
- data/lib/gooddata/models/user_filters/variable_user_filter.rb +14 -0
- data/lib/gooddata/rest/client.rb +32 -21
- data/lib/gooddata/rest/connection.rb +283 -11
- data/lib/gooddata/rest/connections/rest_client_connection.rb +47 -109
- data/lib/gooddata/version.rb +1 -1
- data/spec/data/column_based_permissions.csv +7 -0
- data/spec/data/column_based_permissions2.csv +6 -0
- data/spec/data/hello_world_process/hello_world.rb +3 -1
- data/spec/data/line_based_permissions.csv +3 -0
- data/spec/data/m_n_model/blueprint.json +76 -0
- data/spec/data/{model_view.json → wire_models/model_view.json} +0 -0
- data/spec/data/wire_models/nu_model.json +3046 -0
- data/spec/helpers/process_helper.rb +2 -2
- data/spec/helpers/project_helper.rb +29 -0
- data/spec/helpers/schedule_helper.rb +1 -1
- data/spec/integration/command_datawarehouse_spec.rb +32 -0
- data/spec/integration/create_project_spec.rb +0 -1
- data/spec/integration/full_process_schedule_spec.rb +13 -5
- data/spec/integration/full_project_spec.rb +2 -1
- data/spec/integration/over_to_user_filters_spec.rb +92 -0
- data/spec/integration/project_spec.rb +233 -0
- data/spec/integration/rest_spec.rb +209 -0
- data/spec/integration/user_filters_spec.rb +193 -0
- data/spec/integration/variables_spec.rb +196 -0
- data/spec/unit/commands/command_auth_spec.rb +0 -7
- data/spec/unit/commands/command_process_spec.rb +10 -13
- data/spec/unit/core/connection_spec.rb +0 -19
- data/spec/unit/helpers/global_helpers_spec.rb +57 -0
- data/spec/unit/models/domain_spec.rb +80 -40
- data/spec/unit/models/from_wire_spec.rb +8 -1
- data/spec/unit/models/params_spec.rb +6 -6
- data/spec/unit/models/profile_spec.rb +23 -22
- data/spec/unit/models/project_blueprint_spec.rb +1 -6
- data/spec/unit/models/project_spec.rb +331 -286
- data/spec/unit/models/schedule_spec.rb +39 -14
- data/spec/unit/models/user_filters_spec.rb +89 -0
- data/spec/unit/models/variable_spec.rb +259 -0
- metadata +31 -7
- data/lib/gooddata/rest/connections/dummy_connection.rb +0 -52
- data/spec/unit/core/rest_spec.rb +0 -106
@@ -17,8 +17,6 @@ module GoodData
|
|
17
17
|
include GoodData::Mixin::RestResource
|
18
18
|
root_key :metric
|
19
19
|
|
20
|
-
PARSE_MAQL_OBJECT_REGEXP = /\[([^\]]+)\]/
|
21
|
-
|
22
20
|
class << self
|
23
21
|
# Method intended to get all objects of that type in a specified project
|
24
22
|
#
|
@@ -212,27 +210,7 @@ module GoodData
|
|
212
210
|
# Looks up the readable values of the objects used inside of MAQL epxpressions. Labels and elements titles are based on the primary label.
|
213
211
|
# @return [String] Ther resulting MAQL like expression
|
214
212
|
def pretty_expression
|
215
|
-
|
216
|
-
:client => client,
|
217
|
-
:project => project
|
218
|
-
}
|
219
|
-
|
220
|
-
temp = expression.dup
|
221
|
-
pairs = expression.scan(PARSE_MAQL_OBJECT_REGEXP).pmap do |uri|
|
222
|
-
uri = uri.first
|
223
|
-
if uri =~ /elements/
|
224
|
-
[uri, Attribute.find_element_value(uri, opts)]
|
225
|
-
else
|
226
|
-
[uri, GoodData::MdObject[uri, opts].title]
|
227
|
-
end
|
228
|
-
end
|
229
|
-
|
230
|
-
pairs.each do |el|
|
231
|
-
uri = el[0]
|
232
|
-
obj = el[1]
|
233
|
-
temp.sub!(uri, obj)
|
234
|
-
end
|
235
|
-
temp
|
213
|
+
SmallGoodZilla.pretty_print(expression, client: client, project: project)
|
236
214
|
end
|
237
215
|
end
|
238
216
|
end
|
@@ -120,8 +120,8 @@ module GoodData
|
|
120
120
|
#
|
121
121
|
# @return [String] Returns data
|
122
122
|
def export(format)
|
123
|
-
result =
|
124
|
-
result1 =
|
123
|
+
result = client.post('/gdc/xtab2/executor3', 'report_req' => { 'report' => uri })
|
124
|
+
result1 = client.post('/gdc/exporter/executor', :result_req => { :format => format, :result => result })
|
125
125
|
GoodData.poll_on_code(result1['uri'], process: false)
|
126
126
|
end
|
127
127
|
|
@@ -44,6 +44,12 @@ module GoodData
|
|
44
44
|
}
|
45
45
|
end
|
46
46
|
|
47
|
+
def create_filters_part(filters)
|
48
|
+
filters.select { |f| f.class == GoodData::Variable }.map do |v|
|
49
|
+
{ expression: "[#{v.uri}]" }
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
47
53
|
def create_part(stuff)
|
48
54
|
stuff = Array(stuff)
|
49
55
|
parts = stuff.reduce([]) do |memo, item|
|
@@ -146,7 +152,6 @@ module GoodData
|
|
146
152
|
}
|
147
153
|
}
|
148
154
|
uri = "/gdc/app/projects/#{project.pid}/execute"
|
149
|
-
|
150
155
|
client.post(uri, data)
|
151
156
|
end
|
152
157
|
|
@@ -170,6 +175,13 @@ module GoodData
|
|
170
175
|
end
|
171
176
|
end
|
172
177
|
|
178
|
+
# Return true if the report definition is a chart
|
179
|
+
#
|
180
|
+
# @return [Boolean] Return true if report definition is a chart
|
181
|
+
def chart?
|
182
|
+
!table?
|
183
|
+
end
|
184
|
+
|
173
185
|
def create(options = { :client => GoodData.connection, :project => GoodData.project })
|
174
186
|
client = options[:client]
|
175
187
|
fail ArgumentError, 'No :client specified' if client.nil?
|
@@ -182,6 +194,7 @@ module GoodData
|
|
182
194
|
|
183
195
|
left = Array(options[:left])
|
184
196
|
top = Array(options[:top])
|
197
|
+
filters = options[:filters] || []
|
185
198
|
|
186
199
|
left = ReportDefinition.find(left, options)
|
187
200
|
top = ReportDefinition.find(top, options)
|
@@ -204,7 +217,7 @@ module GoodData
|
|
204
217
|
'rows' => ReportDefinition.create_part(left)
|
205
218
|
},
|
206
219
|
'format' => 'grid',
|
207
|
-
'filters' =>
|
220
|
+
'filters' => ReportDefinition.create_filters_part(filters)
|
208
221
|
},
|
209
222
|
'meta' => {
|
210
223
|
'tags' => '',
|
@@ -373,5 +386,12 @@ module GoodData
|
|
373
386
|
end
|
374
387
|
self
|
375
388
|
end
|
389
|
+
|
390
|
+
# Return true if the report definition is a table
|
391
|
+
#
|
392
|
+
# @return [Boolean] Return true if report definition is a table
|
393
|
+
def table?
|
394
|
+
content['format'] == 'grid'
|
395
|
+
end
|
376
396
|
end
|
377
397
|
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require_relative '../metadata'
|
4
|
+
|
5
|
+
require_relative 'metadata'
|
6
|
+
|
7
|
+
module GoodData
|
8
|
+
class Variable < MdObject
|
9
|
+
root_key :prompt
|
10
|
+
|
11
|
+
class << self
|
12
|
+
# Method intended to get all objects of that type in a specified project
|
13
|
+
#
|
14
|
+
# @param options [Hash] the options hash
|
15
|
+
# @option options [Boolean] :full if passed true the subclass can decide to pull in full objects. This is desirable from the usability POV but unfortunately has negative impact on performance so it is not the default
|
16
|
+
# @return [Array<GoodData::MdObject> | Array<Hash>] Return the appropriate metadata objects or their representation
|
17
|
+
def all(options = { :client => GoodData.connection, :project => GoodData.project })
|
18
|
+
query('prompts', Variable, options)
|
19
|
+
end
|
20
|
+
|
21
|
+
def create(data, options = { :client => GoodData.connection, :project => GoodData.project })
|
22
|
+
title = data[:title]
|
23
|
+
project = options[:project]
|
24
|
+
c = client(options)
|
25
|
+
attribute = project.attributes(data[:attribute])
|
26
|
+
|
27
|
+
payload = {
|
28
|
+
'prompt' => {
|
29
|
+
'content' => {
|
30
|
+
'attribute' => attribute.uri,
|
31
|
+
'type' => 'filter'
|
32
|
+
},
|
33
|
+
'meta' => {
|
34
|
+
'tags' => '',
|
35
|
+
'deprecated' => '0',
|
36
|
+
'summary' => '',
|
37
|
+
'title' => title,
|
38
|
+
'category' => 'prompt'
|
39
|
+
}
|
40
|
+
}
|
41
|
+
}
|
42
|
+
c.create(self, payload, project: project)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Retrieves variable values
|
47
|
+
#
|
48
|
+
# @return [Array<GoodData::VariableUserFilter>] Values of variable
|
49
|
+
def values
|
50
|
+
payload = {
|
51
|
+
variablesSearch: {
|
52
|
+
variables: [
|
53
|
+
uri
|
54
|
+
],
|
55
|
+
context: []
|
56
|
+
}
|
57
|
+
}
|
58
|
+
client.post("/gdc/md/#{project.pid}/variables/search", payload)['variables'].map { |f| client.create(GoodData::VariableUserFilter, f, project: project) }
|
59
|
+
end
|
60
|
+
|
61
|
+
# Retrieves variable values and returns only those related to user
|
62
|
+
#
|
63
|
+
# @return [Array<GoodData::VariableUserFilter>] Values of variable related to user
|
64
|
+
def user_values
|
65
|
+
values.select { |x| x.level == :user }
|
66
|
+
end
|
67
|
+
|
68
|
+
# Retrieves variable values and returns only those related to project
|
69
|
+
#
|
70
|
+
# @return [Array<GoodData::VariableUserFilter>] Values of variable related to project
|
71
|
+
def project_values
|
72
|
+
values.select { |x| x.level == :project }
|
73
|
+
end
|
74
|
+
|
75
|
+
# Deletes all the values and eventually the variable itself
|
76
|
+
def delete
|
77
|
+
values.pmap(&:delete)
|
78
|
+
super
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -6,6 +6,7 @@ require_relative 'metadata/metadata'
|
|
6
6
|
|
7
7
|
require_relative 'links'
|
8
8
|
require_relative 'module_constants'
|
9
|
+
require_relative 'user_filters/user_filters'
|
9
10
|
|
10
11
|
require 'fileutils'
|
11
12
|
require 'multi_json'
|
@@ -122,7 +123,7 @@ module GoodData
|
|
122
123
|
|
123
124
|
if res['taskStatus'] == 'ERROR' # rubocop:disable Style/GuardClause
|
124
125
|
s = StringIO.new
|
125
|
-
client.download_from_user_webdav(File.basename(dir) + '/upload_status.json', s)
|
126
|
+
client.download_from_user_webdav(File.basename(dir) + '/upload_status.json', s, :client => client, :project => project)
|
126
127
|
js = MultiJson.load(s.string)
|
127
128
|
fail "Load Failed with error #{JSON.pretty_generate(js)}"
|
128
129
|
end
|
@@ -51,7 +51,6 @@ module GoodData
|
|
51
51
|
Process[:all]
|
52
52
|
end
|
53
53
|
|
54
|
-
# TODO: Check the params.
|
55
54
|
def with_deploy(dir, options = {}, &block)
|
56
55
|
client = options[:client]
|
57
56
|
fail ArgumentError, 'No :client specified' if client.nil?
|
@@ -87,7 +86,7 @@ module GoodData
|
|
87
86
|
project = GoodData::Project[p, opts]
|
88
87
|
fail ArgumentError, 'Wrong :project specified' if project.nil?
|
89
88
|
|
90
|
-
zip_and_upload
|
89
|
+
zip_and_upload(path, files_to_exclude, opts)
|
91
90
|
end
|
92
91
|
|
93
92
|
# Deploy a new process or redeploy existing one.
|
@@ -119,7 +118,7 @@ module GoodData
|
|
119
118
|
verbose = options[:verbose] || false
|
120
119
|
puts HighLine.color("Deploying #{path}", HighLine::BOLD) if verbose
|
121
120
|
|
122
|
-
deployed_path = Process.upload_package(path, files_to_exclude, :
|
121
|
+
deployed_path = Process.upload_package(path, files_to_exclude, client: client, project: project)
|
123
122
|
data = {
|
124
123
|
:process => {
|
125
124
|
:name => deploy_name,
|
@@ -197,7 +196,7 @@ module GoodData
|
|
197
196
|
# @option options [String] :name Readable name of the process
|
198
197
|
# @option options [Boolean] :verbose (false) Switch on verbose mode for detailed logging
|
199
198
|
def deploy(path, options = {})
|
200
|
-
Process.deploy(path,
|
199
|
+
Process.deploy(path, client: client, process_id: process_id, :project => project).merge(options)
|
201
200
|
end
|
202
201
|
|
203
202
|
# Downloads the process from S3 in a zipped form.
|
@@ -68,26 +68,13 @@ module GoodData
|
|
68
68
|
c.factory.create(Profile, response)
|
69
69
|
end
|
70
70
|
|
71
|
-
# Apply changes to object.
|
72
|
-
#
|
73
|
-
# @param obj [GoodData::Profile] Object to be modified
|
74
|
-
# @param changes [Hash] Hash with modifications
|
75
|
-
# @return [GoodData::Profile] Modified object
|
76
|
-
def apply(obj, changes)
|
77
|
-
changes.each do |param, val|
|
78
|
-
next unless ASSIGNABLE_MEMBERS.include? param
|
79
|
-
obj.send("#{param}=", val)
|
80
|
-
end
|
81
|
-
obj
|
82
|
-
end
|
83
|
-
|
84
71
|
# Creates new instance from hash with attributes
|
85
72
|
#
|
86
73
|
# @param attributes [Hash] Hash with initial attributes
|
87
74
|
# @return [GoodData::Profile] New profile instance
|
88
75
|
def create(attributes)
|
89
76
|
json = EMPTY_OBJECT.dup
|
90
|
-
res = GoodData::Profile
|
77
|
+
res = client.create(GoodData::Profile, json)
|
91
78
|
|
92
79
|
attributes.each do |k, v|
|
93
80
|
res.send("#{k}=", v) if ASSIGNABLE_MEMBERS.include? k
|
@@ -97,62 +84,20 @@ module GoodData
|
|
97
84
|
res
|
98
85
|
end
|
99
86
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
87
|
+
def diff(item_1, item_2)
|
88
|
+
x = diff_list([item_1], [item_2])
|
89
|
+
return {} if x[:changed].empty?
|
90
|
+
x[:changed].first[:diff]
|
104
91
|
end
|
105
92
|
|
106
|
-
|
107
|
-
|
108
|
-
# @param user1 [GoodData::Profile] Original user
|
109
|
-
# @param user2 [GoodData::Profile] User to compare with
|
110
|
-
# @return [Hash] Hash representing diff
|
111
|
-
def diff(user1, user2)
|
112
|
-
res = {}
|
113
|
-
ASSIGNABLE_MEMBERS.each do |k|
|
114
|
-
l_value = user1.send("#{k}")
|
115
|
-
r_value = user2.send("#{k}")
|
116
|
-
res[k] = r_value if l_value != r_value
|
117
|
-
end
|
118
|
-
res
|
93
|
+
def diff_list(list_1, list_2)
|
94
|
+
GoodData::Helpers.diff(list_1, list_2, key: :login)
|
119
95
|
end
|
120
96
|
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
:added => [],
|
126
|
-
:removed => [],
|
127
|
-
:changed => []
|
128
|
-
}
|
129
|
-
|
130
|
-
list2.each do |user_new|
|
131
|
-
user_existing = tmp[user_new.email]
|
132
|
-
if user_existing.nil?
|
133
|
-
res[:added] << user_new
|
134
|
-
next
|
135
|
-
end
|
136
|
-
|
137
|
-
next if user_existing == user_new
|
138
|
-
|
139
|
-
diff = self.diff(user_existing, user_new)
|
140
|
-
res[:changed] << {
|
141
|
-
:user => user_existing,
|
142
|
-
:diff => diff
|
143
|
-
}
|
144
|
-
end
|
145
|
-
|
146
|
-
tmp = Hash[list2.map { |v| [v.email, v] }]
|
147
|
-
list1.each do |user_existing|
|
148
|
-
user_new = tmp[user_existing.email]
|
149
|
-
if user_new.nil?
|
150
|
-
res[:removed] << user_existing
|
151
|
-
next
|
152
|
-
end
|
153
|
-
end
|
154
|
-
|
155
|
-
res
|
97
|
+
# Gets user currently logged in
|
98
|
+
# @return [GoodData::Profile] User currently logged-in
|
99
|
+
def current
|
100
|
+
client.user
|
156
101
|
end
|
157
102
|
end
|
158
103
|
|
@@ -169,13 +114,8 @@ module GoodData
|
|
169
114
|
# @param right [GoodData::Profile] Project to compare with
|
170
115
|
# @return [Boolean] True if same else false
|
171
116
|
def ==(other)
|
172
|
-
|
173
|
-
|
174
|
-
l_val = send("#{k}")
|
175
|
-
r_val = other.send("#{k}")
|
176
|
-
res = false if l_val != r_val
|
177
|
-
end
|
178
|
-
res
|
117
|
+
return false unless other.respond_to?(:to_hash)
|
118
|
+
to_hash == other.to_hash
|
179
119
|
end
|
180
120
|
|
181
121
|
# Checks objects for non-equality
|
@@ -190,9 +130,9 @@ module GoodData
|
|
190
130
|
#
|
191
131
|
# @param changes [Hash] Hash with modifications
|
192
132
|
# @return [GoodData::Profile] Modified object
|
193
|
-
def apply(changes)
|
194
|
-
|
195
|
-
end
|
133
|
+
# def apply(changes)
|
134
|
+
# GoodData::Profile.apply(self, changes)
|
135
|
+
# end
|
196
136
|
|
197
137
|
# Gets the company name
|
198
138
|
#
|
@@ -374,7 +314,7 @@ module GoodData
|
|
374
314
|
|
375
315
|
if uri && !uri.empty?
|
376
316
|
url = "/gdc/account/profile/#{obj_id}"
|
377
|
-
@json =
|
317
|
+
@json = client.put url, raw
|
378
318
|
@dirty = false
|
379
319
|
end
|
380
320
|
end
|
@@ -406,14 +346,42 @@ module GoodData
|
|
406
346
|
#
|
407
347
|
# @return [String] Resource URI
|
408
348
|
def uri
|
409
|
-
@json
|
349
|
+
GoodData::Helpers.get_path(@json, %w(accountSetting links self))
|
350
|
+
# @json['accountSetting']['links']['self']
|
410
351
|
end
|
411
352
|
|
412
|
-
|
353
|
+
def data
|
354
|
+
data = @json || {}
|
355
|
+
data['accountSetting'] || {}
|
356
|
+
end
|
413
357
|
|
414
|
-
def
|
415
|
-
|
416
|
-
|
358
|
+
def links
|
359
|
+
data['links'] || {}
|
360
|
+
end
|
361
|
+
|
362
|
+
def content
|
363
|
+
keys = (data.keys - ['links'])
|
364
|
+
data.slice(*keys)
|
365
|
+
end
|
366
|
+
|
367
|
+
def name
|
368
|
+
(first_name || '') + (last_name || '')
|
369
|
+
end
|
370
|
+
|
371
|
+
def to_hash
|
372
|
+
tmp = content.merge(uri: uri).symbolize_keys
|
373
|
+
[
|
374
|
+
[:companyName, :company],
|
375
|
+
[:phoneNumber, :phone],
|
376
|
+
[:firstName, :first_name],
|
377
|
+
[:lastName, :last_name],
|
378
|
+
[:authenticationModes, :authentication_modes]
|
379
|
+
].each do |vals|
|
380
|
+
wire, rb = vals
|
381
|
+
tmp[rb] = tmp[wire]
|
382
|
+
tmp.delete(wire)
|
383
|
+
end
|
384
|
+
tmp
|
417
385
|
end
|
418
386
|
end
|
419
387
|
end
|
@@ -152,6 +152,10 @@ module GoodData
|
|
152
152
|
end
|
153
153
|
end
|
154
154
|
|
155
|
+
# Creates a metric in a project
|
156
|
+
#
|
157
|
+
# @param [options] Optional report options
|
158
|
+
# @return [GoodData::Report] Instance of new report
|
155
159
|
def add_metric(metric, options = {})
|
156
160
|
default = { client: client, project: self }
|
157
161
|
if metric.is_a?(String)
|
@@ -189,7 +193,7 @@ module GoodData
|
|
189
193
|
#
|
190
194
|
# @return [Boolean] True if user has admin role in the project, false otherwise.
|
191
195
|
def am_i_admin?
|
192
|
-
user_has_role?(
|
196
|
+
user_has_role?(client.user, 'admin')
|
193
197
|
end
|
194
198
|
|
195
199
|
# Helper for getting attributes of a project
|
@@ -252,6 +256,10 @@ module GoodData
|
|
252
256
|
end
|
253
257
|
end
|
254
258
|
|
259
|
+
def dimensions(id = :all)
|
260
|
+
GoodData::Dimension[id, client: client, project: self]
|
261
|
+
end
|
262
|
+
|
255
263
|
# Export a clone from a project to be later imported.
|
256
264
|
# If you do not want to do anything special and you do not need fine grained
|
257
265
|
# controle use clone method which does all the heavy lifting for you.
|
@@ -302,17 +310,20 @@ module GoodData
|
|
302
310
|
end
|
303
311
|
|
304
312
|
def compute_report(spec = {})
|
305
|
-
GoodData::ReportDefinition.execute(spec.merge(:
|
313
|
+
GoodData::ReportDefinition.execute(spec.merge(client: client, project: self))
|
306
314
|
end
|
307
315
|
|
308
316
|
def compute_metric(expression)
|
309
|
-
GoodData::Metric.xexecute(expression, :
|
317
|
+
GoodData::Metric.xexecute(expression, client: client, project: self)
|
310
318
|
end
|
311
319
|
|
312
320
|
def create_schedule(process, date, executable, options = {})
|
313
|
-
GoodData::Schedule.create(process, date, executable, options.merge(:
|
321
|
+
GoodData::Schedule.create(process, date, executable, options.merge(client: client, project: self))
|
314
322
|
end
|
315
323
|
|
324
|
+
def create_variable(data)
|
325
|
+
GoodData::Variable.create(data, client: client, project: self)
|
326
|
+
end
|
316
327
|
# Helper for getting dashboards of a project
|
317
328
|
#
|
318
329
|
# @param [String | Number | Object] Anything that you can pass to GoodData::Dashboard[id]
|
@@ -325,6 +336,10 @@ module GoodData
|
|
325
336
|
blueprint.datasets
|
326
337
|
end
|
327
338
|
|
339
|
+
def data_permissions
|
340
|
+
GoodData::MandatoryUserFilter.all(client: client, project: self)
|
341
|
+
end
|
342
|
+
|
328
343
|
# Deletes project
|
329
344
|
def delete
|
330
345
|
fail "Project '#{title}' with id #{uri} is already deleted" if state == :deleted
|
@@ -401,6 +416,13 @@ module GoodData
|
|
401
416
|
GoodData::Attribute.find_element_value(uri, client: client, project: self)
|
402
417
|
end
|
403
418
|
|
419
|
+
# Get WebDav directory for project data
|
420
|
+
# @return [String]
|
421
|
+
def get_project_webdav_path(_file)
|
422
|
+
u = URI(links['uploads'])
|
423
|
+
URI.join(u.to_s.chomp(u.path.to_s), '/project-uploads/', "#{pid}/")
|
424
|
+
end
|
425
|
+
|
404
426
|
# Gets project role by its identifier
|
405
427
|
#
|
406
428
|
# @param [String] role_name Title of role to look for
|
@@ -454,122 +476,55 @@ module GoodData
|
|
454
476
|
nil
|
455
477
|
end
|
456
478
|
|
457
|
-
# Gets user by its
|
479
|
+
# Gets user by its login or uri in various shapes
|
480
|
+
# It does not find by other information because that is not unique. If you want to search by name or email please
|
481
|
+
# use fuzzy_get_user.
|
458
482
|
#
|
459
483
|
# @param [String] name Name to look for
|
460
484
|
# @param [Array<GoodData::User>]user_list Optional cached list of users used for look-ups
|
461
485
|
# @return [GoodDta::Membership] User
|
462
|
-
def get_user(
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
486
|
+
def get_user(slug, user_list = users)
|
487
|
+
search_crit = if slug.respond_to?(:login)
|
488
|
+
slug.login || slug.uri
|
489
|
+
elsif slug.is_a?(Hash)
|
490
|
+
slug[:login] || slug[:uri]
|
491
|
+
else
|
492
|
+
slug
|
493
|
+
end
|
494
|
+
return nil unless search_crit
|
495
|
+
user_list.find do |user|
|
496
|
+
user.uri == search_crit.downcase ||
|
497
|
+
user.login.downcase == search_crit.downcase
|
470
498
|
end
|
471
|
-
nil
|
472
499
|
end
|
473
500
|
|
474
|
-
#
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
##########################
|
481
|
-
domains = {}
|
482
|
-
current_users = users
|
483
|
-
role_list = roles
|
484
|
-
|
485
|
-
##########################
|
486
|
-
# Load users from CSV
|
487
|
-
##########################
|
488
|
-
new_users = GoodData::Helpers.csv_read(opts) do |row|
|
489
|
-
json = {}
|
490
|
-
if block_given?
|
491
|
-
json = yield row
|
492
|
-
else
|
493
|
-
json = {
|
494
|
-
'user' => {
|
495
|
-
'content' => {
|
496
|
-
'email' => row[0],
|
497
|
-
'login' => row[1],
|
498
|
-
'firstname' => row[2],
|
499
|
-
'lastname' => row[3]
|
500
|
-
},
|
501
|
-
'meta' => {}
|
502
|
-
}
|
503
|
-
}
|
504
|
-
end
|
505
|
-
|
506
|
-
GoodData::User.new(json)
|
507
|
-
end
|
508
|
-
|
509
|
-
##########################
|
510
|
-
# Diff users
|
511
|
-
##########################
|
512
|
-
diff = GoodData::User.diff_list(current_users, new_users)
|
513
|
-
|
514
|
-
##########################
|
515
|
-
# Create new users
|
516
|
-
##########################
|
517
|
-
diff[:added].map do |user|
|
518
|
-
# TODO: Add user here
|
519
|
-
domain_name = user.json['user']['content']['domain']
|
520
|
-
|
521
|
-
# Lookup for domain in cache'
|
522
|
-
domain = domains[domain_name]
|
523
|
-
|
524
|
-
# Get domain info from REST, add to cache
|
525
|
-
if domain.nil?
|
526
|
-
domain = {
|
527
|
-
:domain => GoodData::Domain[domain_name],
|
528
|
-
:users => GoodData::Domain[domain_name].users
|
529
|
-
}
|
530
|
-
|
531
|
-
domain[:users_map] = Hash[domain[:users].map { |u| [u.email, u] }]
|
532
|
-
domains[domain_name] = domain
|
533
|
-
end
|
534
|
-
|
535
|
-
# Check if user exists in domain
|
536
|
-
domain_user = domain[:users_map][user.email]
|
537
|
-
|
538
|
-
# Create domain user if needed
|
539
|
-
unless domain_user
|
540
|
-
password = user.json['user']['content']['password']
|
541
|
-
|
542
|
-
# Fill necessary user data
|
543
|
-
user_data = {
|
544
|
-
:login => user.login,
|
545
|
-
:firstName => user.first_name,
|
546
|
-
:lastName => user.last_name,
|
547
|
-
:password => password,
|
548
|
-
:verifyPassword => password,
|
549
|
-
:email => user.login
|
550
|
-
}
|
551
|
-
|
552
|
-
# Add created user to cache
|
553
|
-
domain_user = domain[:domain].add_user(user_data)
|
554
|
-
domain[:users] << domain_user
|
555
|
-
domain[:users_map][user.email] = domain_user
|
556
|
-
end
|
501
|
+
# Get WebDav directory for user data
|
502
|
+
# @return [String]
|
503
|
+
def get_user_webdav_path(_file)
|
504
|
+
u = URI(links['uploads'])
|
505
|
+
URI.join(u.to_s.chomp(u.path.to_s), '/uploads/')
|
506
|
+
end
|
557
507
|
|
558
|
-
|
559
|
-
|
560
|
-
role = get_role_by_identifier(role_name, role_list)
|
561
|
-
next if role.nil?
|
508
|
+
# Gets user by its email, full_name, login or uri
|
509
|
+
alias_method :member, :get_user
|
562
510
|
|
563
|
-
|
564
|
-
|
511
|
+
# Gets user by its email, full_name, login or uri.
|
512
|
+
#
|
513
|
+
# @param [String] name Name to look for
|
514
|
+
# @param [Array<GoodData::User>]user_list Optional cached list of users used for look-ups
|
515
|
+
# @return [GoodDta::Membership] User
|
516
|
+
def fuzzy_get_user(name, user_list = users)
|
517
|
+
return name if name.instance_of?(GoodData::Membership)
|
518
|
+
return member(name) if name.instance_of?(GoodData::Profile)
|
519
|
+
name = name.is_a?(Hash) ? name[:login] || name[:uri] : name
|
520
|
+
return nil unless name
|
521
|
+
name.downcase!
|
522
|
+
user_list.select do |user|
|
523
|
+
user.uri.downcase == name ||
|
524
|
+
user.login.downcase == name ||
|
525
|
+
user.email.downcase == name
|
565
526
|
end
|
566
|
-
|
567
|
-
##########################
|
568
|
-
# Remove old users
|
569
|
-
##########################
|
570
|
-
# diff[:removed].map do |user|
|
571
|
-
# user.disable(self)
|
572
|
-
# end
|
527
|
+
nil
|
573
528
|
end
|
574
529
|
|
575
530
|
# Checks whether user has particular role in given proejct
|
@@ -665,20 +620,6 @@ module GoodData
|
|
665
620
|
@md ||= client.create(Links, client.get(data['links']['metadata']))
|
666
621
|
end
|
667
622
|
|
668
|
-
# Gets membership for profile specified
|
669
|
-
#
|
670
|
-
# @param [GoodData::Profile] profile - Profile to be checked
|
671
|
-
# @param [Array<GoodData::Membership>] list Optional list of members to check against
|
672
|
-
# @return [GoodData::Membership] Membership if found
|
673
|
-
def member(profile, list = members)
|
674
|
-
if profile.is_a? String
|
675
|
-
return list.find do |m|
|
676
|
-
m.uri == profile || m.login == profile
|
677
|
-
end
|
678
|
-
end
|
679
|
-
list.find { |m| m.login == profile.login }
|
680
|
-
end
|
681
|
-
|
682
623
|
# Get data from project specific metadata storage
|
683
624
|
#
|
684
625
|
# @param [Symbol | String] :all or nothing for all keys or a string for value of specific key
|
@@ -719,6 +660,10 @@ module GoodData
|
|
719
660
|
!member(profile, list).nil?
|
720
661
|
end
|
721
662
|
|
663
|
+
def members?(profiles, list = members)
|
664
|
+
profiles.map { |p| member?(p, list) }
|
665
|
+
end
|
666
|
+
|
722
667
|
# Gets raw resource ID
|
723
668
|
#
|
724
669
|
# @return [String] Raw resource ID
|
@@ -850,7 +795,7 @@ module GoodData
|
|
850
795
|
tmp = client.get(url)
|
851
796
|
tmp['projectRoles']['roles'].pmap do |role_url|
|
852
797
|
json = client.get role_url
|
853
|
-
client.create(GoodData::ProjectRole, json)
|
798
|
+
client.create(GoodData::ProjectRole, json, project: self)
|
854
799
|
end
|
855
800
|
end
|
856
801
|
|
@@ -926,103 +871,81 @@ module GoodData
|
|
926
871
|
# List of users in project
|
927
872
|
#
|
928
873
|
# @return [Array<GoodData::User>] List of users
|
929
|
-
def users
|
930
|
-
|
931
|
-
|
932
|
-
|
874
|
+
def users(opts = { offset: 0, limit: 100 })
|
875
|
+
result = []
|
876
|
+
|
877
|
+
# TODO: @korczis, review this after WA-3953 get fixed
|
878
|
+
offset = 0 || opts[:offset]
|
879
|
+
uri = "/gdc/projects/#{pid}/users?offset=#{offset}&limit=#{opts[:limit]}"
|
880
|
+
loop do
|
881
|
+
break unless uri
|
882
|
+
tmp = client(opts).get(uri)
|
883
|
+
tmp['users'].each do |user|
|
884
|
+
result << client.factory.create(GoodData::Membership, user, project: self)
|
885
|
+
end
|
886
|
+
offset += opts[:limit]
|
887
|
+
if tmp['users'].length == opts[:limit]
|
888
|
+
uri = "/gdc/projects/#{pid}/users?offset=#{offset}&limit=#{opts[:limit]}"
|
889
|
+
else
|
890
|
+
uri = nil
|
891
|
+
end
|
933
892
|
end
|
893
|
+
|
894
|
+
opts[:all] ? result : result.select(&:enabled?).reject(&:deleted?)
|
934
895
|
end
|
935
896
|
|
936
897
|
alias_method :members, :users
|
937
898
|
|
938
|
-
def
|
939
|
-
|
940
|
-
|
941
|
-
# TODO: Add user here
|
942
|
-
domain_name = user.json['user']['content']['domain']
|
943
|
-
|
944
|
-
# Lookup for domain in cache'
|
945
|
-
domain = domains[domain_name]
|
946
|
-
|
947
|
-
# Get domain info from REST, add to cache
|
948
|
-
if domain.nil?
|
949
|
-
d = GoodData::Domain[domain_name, { :client => client }]
|
950
|
-
domain = {
|
951
|
-
:domain => d,
|
952
|
-
:users => d.users(:client => client)
|
953
|
-
}
|
899
|
+
def whitelist_users(new_users, users_list, whitelist)
|
900
|
+
# whitelist_users
|
901
|
+
return [new_users, users_list] unless whitelist
|
954
902
|
|
955
|
-
|
956
|
-
|
957
|
-
|
903
|
+
whitelist_proc = proc do |user|
|
904
|
+
whitelist.any? { |wl| wl.is_a?(Regexp) ? user[:login] =~ wl : user[:login].include?(wl) }
|
905
|
+
end
|
958
906
|
|
959
|
-
|
960
|
-
|
961
|
-
fail ArgumentError, "Trying to add user '#{user.login}' which is not valid user in domain '#{domain_name}'" if domain_user.nil?
|
907
|
+
[new_users.reject(&whitelist_proc), users_list.reject(&whitelist_proc)]
|
908
|
+
end
|
962
909
|
|
963
|
-
|
964
|
-
|
965
|
-
|
966
|
-
|
910
|
+
# Imports users
|
911
|
+
def import_users(new_users, options = {})
|
912
|
+
domain = options[:domain]
|
913
|
+
users_list = users.map(&:to_hash)
|
914
|
+
new_users = new_users.map { |x| (x.is_a?(Hash) && x[:user] && x[:user].to_hash.merge(role: x[:role])) || x.to_hash }
|
967
915
|
|
968
|
-
|
969
|
-
set_user_roles(domain_user, [role.uri], role_list)
|
970
|
-
end
|
971
|
-
end
|
916
|
+
whitelisted_new_users, whitelisted_users = whitelist_users(new_users.map(&:to_hash), users_list, options[:whitelists])
|
972
917
|
|
973
|
-
# Imports users from CSV
|
974
|
-
#
|
975
|
-
# # Features
|
976
|
-
# - Create new users
|
977
|
-
# - Delete old users
|
978
|
-
# - Update existing users
|
979
|
-
#
|
980
|
-
# CSV Format
|
981
|
-
# TODO: Describe CSV Format here
|
982
|
-
#
|
983
|
-
# @param path CSV file to be loaded
|
984
|
-
# @param opts Optional additional options
|
985
|
-
def users_import(new_users, domain = nil)
|
986
918
|
# Diff users
|
987
|
-
diff = GoodData::
|
988
|
-
|
919
|
+
diff = GoodData::Helpers.diff(whitelisted_users, whitelisted_new_users, key: :login)
|
920
|
+
results = []
|
989
921
|
# Create domain users
|
990
|
-
|
922
|
+
results.concat domain.create_users(diff[:added])
|
923
|
+
|
924
|
+
# Update domain users
|
925
|
+
domain.create_users(diff[:changed].map { |u| u[:new_obj] })
|
991
926
|
|
992
927
|
# Create new users
|
993
928
|
role_list = roles
|
994
|
-
|
995
|
-
|
996
|
-
# Get changed users objects from hash
|
997
|
-
list = diff[:changed].map do |user|
|
998
|
-
user[:user]
|
999
|
-
end
|
1000
|
-
|
1001
|
-
# Join list of changed users with 'same' users
|
1002
|
-
list = list.zip(diff[:same]).flatten.compact
|
1003
|
-
|
1004
|
-
new_users_map = Hash[new_users.map { |u| [u.email, u] }]
|
1005
|
-
|
1006
|
-
# Create list with user, desired_roles hashes
|
1007
|
-
list = list.map do |user|
|
929
|
+
u = diff[:added].map do |x|
|
1008
930
|
{
|
1009
|
-
:
|
1010
|
-
:
|
931
|
+
user: x,
|
932
|
+
role: x[:role] || x[:roles]
|
1011
933
|
}
|
1012
934
|
end
|
935
|
+
results.concat create_users(u, roles: role_list, domain: domain)
|
1013
936
|
|
1014
|
-
# Update existing users
|
1015
|
-
|
937
|
+
# # Update existing users
|
938
|
+
list = diff[:changed].map { |x| { user: x[:new_obj], role: x[:new_obj][:role] || x[:new_obj][:roles] } }
|
939
|
+
results.concat set_users_roles(list, roles: role_list)
|
1016
940
|
|
1017
941
|
# Remove old users
|
1018
|
-
|
942
|
+
results.concat(disable_users(diff[:removed]))
|
943
|
+
results
|
1019
944
|
end
|
1020
945
|
|
1021
|
-
|
1022
|
-
|
1023
|
-
|
1024
|
-
def users_remove(list)
|
1025
|
-
list.pmap(&:disable)
|
946
|
+
def disable_users(list, options = {})
|
947
|
+
project_users = options[:project_users] || users
|
948
|
+
list.map { |u| get_user(u, project_users) }.pmap(&:disable)
|
1026
949
|
end
|
1027
950
|
|
1028
951
|
# Update user
|
@@ -1030,14 +953,18 @@ module GoodData
|
|
1030
953
|
# @param user User to be updated
|
1031
954
|
# @param desired_roles Roles to be assigned to user
|
1032
955
|
# @param role_list Optional cached list of roles used for lookups
|
1033
|
-
def set_user_roles(
|
1034
|
-
|
1035
|
-
|
1036
|
-
|
1037
|
-
|
956
|
+
def set_user_roles(login, desired_roles, options = {})
|
957
|
+
role_list = options[:roles] || roles
|
958
|
+
domain = client.domain(options[:domain]) if options[:domain]
|
959
|
+
project_users = options[:project_users] || users
|
960
|
+
domain_users = options[:domain_users] || (domain && domain.users)
|
1038
961
|
|
1039
|
-
|
962
|
+
project_user = get_user(login, project_users)
|
963
|
+
domain_user = domain.get_user(login, domain_users) if domain && !project_user
|
964
|
+
user = project_user || domain_user
|
965
|
+
fail ArgumentError, "Invalid user '#{login}' specified" unless user
|
1040
966
|
|
967
|
+
desired_roles = [desired_roles] unless desired_roles.is_a? Array
|
1041
968
|
roles = desired_roles.map do |role_name|
|
1042
969
|
role = get_role(role_name, role_list)
|
1043
970
|
fail ArgumentError, "Invalid role '#{role_name}' specified for user '#{user.email}'" if role.nil?
|
@@ -1057,7 +984,7 @@ module GoodData
|
|
1057
984
|
}
|
1058
985
|
}
|
1059
986
|
|
1060
|
-
client.post
|
987
|
+
client.post(url, payload)
|
1061
988
|
end
|
1062
989
|
|
1063
990
|
alias_method :add_user, :set_user_roles
|
@@ -1066,17 +993,43 @@ module GoodData
|
|
1066
993
|
#
|
1067
994
|
# @param list List of users to be updated
|
1068
995
|
# @param role_list Optional list of cached roles to prevent unnecessary server round-trips
|
1069
|
-
def set_users_roles(list,
|
996
|
+
def set_users_roles(list, options = {})
|
997
|
+
role_list = options[:roles] || roles
|
998
|
+
project_users = options[:project_users] || users
|
999
|
+
domain = options[:domain] && client.domain(options[:domain])
|
1000
|
+
domain_users = domain.nil? ? nil : domain.users
|
1001
|
+
|
1070
1002
|
list.pmap do |user_hash|
|
1071
|
-
|
1072
|
-
|
1073
|
-
|
1074
|
-
|
1075
|
-
|
1076
|
-
|
1003
|
+
begin
|
1004
|
+
user = user_hash[:user]
|
1005
|
+
desired_roles = user_hash[:role] || user_hash[:roles] || 'readOnlyUser'
|
1006
|
+
result = add_user(user, desired_roles, options.merge(domain: domain,
|
1007
|
+
domain_users: domain_users,
|
1008
|
+
roles: role_list,
|
1009
|
+
project_users: project_users))
|
1010
|
+
|
1011
|
+
{
|
1012
|
+
type: :role_set,
|
1013
|
+
user: user,
|
1014
|
+
result: result
|
1015
|
+
}
|
1016
|
+
rescue ArgumentError, RuntimeError => e
|
1017
|
+
{ type: :error, reason: e }
|
1018
|
+
end
|
1077
1019
|
end
|
1078
1020
|
end
|
1079
1021
|
|
1022
|
+
alias_method :add_users, :set_users_roles
|
1023
|
+
alias_method :create_users, :set_users_roles
|
1024
|
+
|
1025
|
+
def add_data_permissions(filters, options = {})
|
1026
|
+
GoodData::UserFilterBuilder.execute_mufs(filters, { client: client, project: self }.merge(options))
|
1027
|
+
end
|
1028
|
+
|
1029
|
+
def add_variable_permissions(filters, var, options = {})
|
1030
|
+
GoodData::UserFilterBuilder.execute_variables(filters, var, { client: client, project: self }.merge(options))
|
1031
|
+
end
|
1032
|
+
|
1080
1033
|
# Run validation on project
|
1081
1034
|
# Valid settins for validation are (default all):
|
1082
1035
|
# ldm - Checks the consistency of LDM objects.
|
@@ -1091,5 +1044,9 @@ module GoodData
|
|
1091
1044
|
body['wTaskStatus'] && body['wTaskStatus']['status'] == 'RUNNING'
|
1092
1045
|
end
|
1093
1046
|
end
|
1047
|
+
|
1048
|
+
def variables(id = :all, options = { client: client, project: self })
|
1049
|
+
GoodData::Variable[id, options]
|
1050
|
+
end
|
1094
1051
|
end
|
1095
1052
|
end
|