gooddata 0.6.24 → 0.6.25
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/.rubocop.yml +54 -0
- data/CHANGELOG.md +3 -0
- data/DEPENDENCIES.md +155 -154
- data/README.md +15 -6
- data/Rakefile +5 -3
- data/dependency_decisions.yml +2 -0
- data/gooddata.gemspec +2 -3
- data/lib/gooddata/cli/cli.rb +1 -3
- data/lib/gooddata/cli/commands/auth_cmd.rb +16 -7
- data/lib/gooddata/cli/commands/project_cmd.rb +16 -178
- data/lib/gooddata/cli/shared.rb +46 -44
- data/lib/gooddata/commands/auth.rb +4 -0
- data/lib/gooddata/commands/project.rb +7 -24
- data/lib/gooddata/exceptions/object_migration.rb +4 -0
- data/lib/gooddata/exceptions/segment_not_empty.rb +18 -0
- data/lib/gooddata/extensions/object.rb +12 -0
- data/lib/gooddata/goodzilla/goodzilla.rb +56 -9
- data/lib/gooddata/helpers/global_helpers.rb +92 -0
- data/lib/gooddata/mixins/md_finders.rb +2 -8
- data/lib/gooddata/mixins/md_grantees.rb +42 -0
- data/lib/gooddata/mixins/md_id_to_uri.rb +1 -8
- data/lib/gooddata/mixins/md_object_id.rb +1 -1
- data/lib/gooddata/mixins/md_object_indexer.rb +5 -8
- data/lib/gooddata/mixins/md_object_query.rb +2 -2
- data/lib/gooddata/mixins/not_group.rb +17 -0
- data/lib/gooddata/mixins/rest_getters.rb +2 -2
- data/lib/gooddata/mixins/rest_resource.rb +1 -0
- data/lib/gooddata/mixins/to_json.rb +11 -0
- data/lib/gooddata/mixins/uri_getter.rb +9 -0
- data/lib/gooddata/models/blueprint/anchor_field.rb +14 -0
- data/lib/gooddata/models/blueprint/project_blueprint.rb +15 -1
- data/lib/gooddata/models/blueprint/to_wire.rb +10 -0
- data/lib/gooddata/models/client.rb +178 -0
- data/lib/gooddata/models/client_synchronization_result.rb +31 -0
- data/lib/gooddata/models/client_synchronization_result_details.rb +41 -0
- data/lib/gooddata/models/datawarehouse.rb +1 -5
- data/lib/gooddata/models/domain.rb +85 -1
- data/lib/gooddata/models/execution.rb +0 -2
- data/lib/gooddata/models/execution_detail.rb +0 -2
- data/lib/gooddata/models/from_wire.rb +10 -0
- data/lib/gooddata/models/invitation.rb +1 -1
- data/lib/gooddata/models/links.rb +1 -1
- data/lib/gooddata/models/membership.rb +10 -6
- data/lib/gooddata/models/metadata.rb +98 -11
- data/lib/gooddata/models/metadata/attribute.rb +6 -7
- data/lib/gooddata/models/metadata/dashboard.rb +41 -75
- data/lib/gooddata/models/metadata/dashboard/dashboard_item.rb +20 -4
- data/lib/gooddata/models/metadata/dashboard/filter_apply_item.rb +37 -0
- data/lib/gooddata/models/metadata/dashboard/filter_item.rb +49 -0
- data/lib/gooddata/models/metadata/dashboard/geo_chart_item.rb +56 -0
- data/lib/gooddata/models/metadata/dashboard/headline_item.rb +56 -0
- data/lib/gooddata/models/metadata/dashboard/iframe_item.rb +46 -0
- data/lib/gooddata/models/metadata/dashboard/report_item.rb +49 -8
- data/lib/gooddata/models/metadata/dashboard/text_item.rb +55 -0
- data/lib/gooddata/models/metadata/dashboard_tab.rb +83 -30
- data/lib/gooddata/models/metadata/dataset.rb +0 -2
- data/lib/gooddata/models/metadata/dimension.rb +1 -3
- data/lib/gooddata/models/metadata/fact.rb +1 -3
- data/lib/gooddata/models/metadata/label.rb +1 -3
- data/lib/gooddata/models/metadata/metric.rb +11 -42
- data/lib/gooddata/models/metadata/report.rb +7 -18
- data/lib/gooddata/models/metadata/report_definition.rb +21 -113
- data/lib/gooddata/models/metadata/scheduled_mail.rb +274 -0
- data/lib/gooddata/models/metadata/scheduled_mail/dashboard_attachment.rb +62 -0
- data/lib/gooddata/models/metadata/scheduled_mail/report_attachment.rb +64 -0
- data/lib/gooddata/models/metadata/variable.rb +8 -2
- data/lib/gooddata/models/model.rb +2 -9
- data/lib/gooddata/models/process.rb +7 -29
- data/lib/gooddata/models/profile.rb +1 -1
- data/lib/gooddata/models/project.rb +131 -167
- data/lib/gooddata/models/project_creator.rb +2 -7
- data/lib/gooddata/models/project_metadata.rb +2 -10
- data/lib/gooddata/models/project_role.rb +4 -10
- data/lib/gooddata/models/report_data_result.rb +3 -5
- data/lib/gooddata/models/schedule.rb +4 -31
- data/lib/gooddata/models/segment.rb +192 -0
- data/lib/gooddata/models/user_filters/mandatory_user_filter.rb +2 -2
- data/lib/gooddata/models/user_filters/user_filter_builder.rb +1 -1
- data/lib/gooddata/models/user_filters/variable_user_filter.rb +11 -0
- data/lib/gooddata/models/user_group.rb +241 -0
- data/lib/gooddata/rest/connection.rb +81 -16
- data/lib/gooddata/rest/object.rb +29 -0
- data/lib/gooddata/rest/object_factory.rb +6 -1
- data/lib/gooddata/rest/resource.rb +7 -1
- data/lib/gooddata/version.rb +1 -1
- data/spec/environment/default.rb +19 -16
- data/spec/environment/develop.rb +10 -10
- data/spec/environment/hotfix.rb +6 -6
- data/spec/environment/production.rb +14 -14
- data/spec/environment/release.rb +6 -6
- data/spec/environment/staging.rb +9 -9
- data/spec/environment/staging_3.rb +14 -15
- data/spec/integration/blueprint_with_grain_spec.rb +72 -0
- data/spec/integration/clients_spec.rb +135 -0
- data/spec/integration/date_dim_switch_spec.rb +142 -0
- data/spec/integration/full_project_spec.rb +3 -3
- data/spec/integration/project_spec.rb +20 -0
- data/spec/integration/segments_spec.rb +141 -0
- data/spec/integration/user_group_spec.rb +127 -0
- data/spec/spec_helper.rb +4 -0
- data/spec/unit/models/domain_spec.rb +7 -1
- data/spec/unit/models/metric_spec.rb +0 -8
- data/spec/unit/models/profile_spec.rb +1 -1
- data/spec/unit/models/report_result_data_spec.rb +6 -0
- metadata +38 -38
- data/lib/gooddata/cli/commands/api_cmd.rb +0 -34
- data/lib/gooddata/cli/commands/console_cmd.rb +0 -40
- data/lib/gooddata/cli/commands/domain_cmd.rb +0 -46
- data/lib/gooddata/cli/commands/process_cmd.rb +0 -145
- data/lib/gooddata/cli/commands/projects_cmd.rb +0 -23
- data/lib/gooddata/cli/commands/run_ruby_cmd.rb +0 -77
- data/lib/gooddata/cli/commands/scaffold_cmd.rb +0 -35
- data/lib/gooddata/cli/commands/user_cmd.rb +0 -24
|
@@ -38,17 +38,12 @@ module GoodData
|
|
|
38
38
|
|
|
39
39
|
def migrate_datasets(spec, opts = {})
|
|
40
40
|
opts = { client: GoodData.connection }.merge(opts)
|
|
41
|
-
client = opts[:client]
|
|
42
41
|
dry_run = opts[:dry_run]
|
|
43
|
-
fail ArgumentError, 'No :client specified' if client.nil?
|
|
44
42
|
|
|
45
|
-
|
|
46
|
-
fail ArgumentError, 'No :project specified' if p.nil?
|
|
43
|
+
client, project = GoodData.get_client_and_project(opts)
|
|
47
44
|
|
|
48
|
-
project = client.projects(p)
|
|
49
|
-
fail ArgumentError, 'Wrong :project specified' if project.nil?
|
|
50
45
|
bp = ProjectBlueprint.new(spec)
|
|
51
|
-
uri = "/gdc/projects/#{project.pid}/model/diff"
|
|
46
|
+
uri = "/gdc/projects/#{project.pid}/model/diff?includeGrain=true"
|
|
52
47
|
result = client.post(uri, bp.to_wire)
|
|
53
48
|
|
|
54
49
|
link = result['asyncTask']['link']['poll']
|
|
@@ -12,11 +12,7 @@ module GoodData
|
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
def [](key, opts = { :client => GoodData.connection, :project => GoodData.project })
|
|
15
|
-
client = opts
|
|
16
|
-
fail ArgumentError, 'No :client specified' if client.nil?
|
|
17
|
-
|
|
18
|
-
project = opts[:project]
|
|
19
|
-
fail ArgumentError, 'No :project specified' if project.nil?
|
|
15
|
+
client, project = GoodData.get_client_and_project(opts)
|
|
20
16
|
|
|
21
17
|
if key == :all
|
|
22
18
|
uri = "/gdc/projects/#{project.pid}/dataload/metadata"
|
|
@@ -43,11 +39,7 @@ module GoodData
|
|
|
43
39
|
end
|
|
44
40
|
|
|
45
41
|
def []=(key, opts = { :client => GoodData.connection, :project => GoodData.project }, val = nil)
|
|
46
|
-
client = opts
|
|
47
|
-
fail ArgumentError, 'No :client specified' if client.nil?
|
|
48
|
-
|
|
49
|
-
project = opts[:project]
|
|
50
|
-
fail ArgumentError, 'No :project specified' if project.nil?
|
|
42
|
+
client, project = GoodData.get_client_and_project(opts)
|
|
51
43
|
|
|
52
44
|
data = {
|
|
53
45
|
:metadataItem => {
|
|
@@ -13,16 +13,10 @@ require_relative '../rest/rest'
|
|
|
13
13
|
require_relative '../mixins/rest_resource'
|
|
14
14
|
|
|
15
15
|
module GoodData
|
|
16
|
-
class ProjectRole <
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
include
|
|
20
|
-
|
|
21
|
-
root_key :projectRole
|
|
22
|
-
|
|
23
|
-
include GoodData::Mixin::Author
|
|
24
|
-
include GoodData::Mixin::Contributor
|
|
25
|
-
include GoodData::Mixin::Timestamps
|
|
16
|
+
class ProjectRole < Rest::Resource
|
|
17
|
+
include Mixin::Author
|
|
18
|
+
include Mixin::Contributor
|
|
19
|
+
include Mixin::Timestamps
|
|
26
20
|
|
|
27
21
|
EMPTY_OBJECT = {
|
|
28
22
|
'projectRole' => {
|
|
@@ -5,15 +5,13 @@
|
|
|
5
5
|
# LICENSE file in the root directory of this source tree.
|
|
6
6
|
|
|
7
7
|
module GoodData
|
|
8
|
-
class ReportDataResult < Rest::
|
|
8
|
+
class ReportDataResult < Rest::Resource
|
|
9
9
|
class << self
|
|
10
10
|
# Does all the needed parsing on the apyload coming from the API and returns an instance of ReportDataResult
|
|
11
11
|
#
|
|
12
12
|
# @param [Hash] data Data coming from the API
|
|
13
13
|
# @return [GoodData::ReportDataResult] Returns new report data result
|
|
14
|
-
def from_xtab(data
|
|
15
|
-
client = options[:client]
|
|
16
|
-
project = options[:project]
|
|
14
|
+
def from_xtab(data)
|
|
17
15
|
top = top_headers(data)
|
|
18
16
|
left = left_headers(data)
|
|
19
17
|
jank = GoodData::Helpers.zeroes(rows(top), cols(left), nil)
|
|
@@ -23,7 +21,7 @@ module GoodData
|
|
|
23
21
|
a = jank.zip(top).map { |x, y| x + y }
|
|
24
22
|
b = left.zip(stuff).map { |x, y| x + y }
|
|
25
23
|
result = a + b
|
|
26
|
-
|
|
24
|
+
ReportDataResult.new(data: result, top: rows(top), left: cols(left))
|
|
27
25
|
end
|
|
28
26
|
|
|
29
27
|
private
|
|
@@ -15,12 +15,6 @@ module GoodData
|
|
|
15
15
|
class Schedule < Rest::Resource
|
|
16
16
|
attr_reader :dirty, :json
|
|
17
17
|
|
|
18
|
-
alias_method :data, :json
|
|
19
|
-
alias_method :raw_data, :json
|
|
20
|
-
|
|
21
|
-
include GoodData::Mixin::RestResource
|
|
22
|
-
root_key :schedule
|
|
23
|
-
|
|
24
18
|
SCHEDULE_TEMPLATE = {
|
|
25
19
|
:schedule => {
|
|
26
20
|
:type => nil,
|
|
@@ -36,14 +30,7 @@ module GoodData
|
|
|
36
30
|
# @param id [String] URL, ID of schedule or :all
|
|
37
31
|
# @return [GoodData::Schedule|Array<GoodData::Schedule>] List of schedules
|
|
38
32
|
def [](id, opts = { :client => GoodData.connection, :project => GoodData.project })
|
|
39
|
-
c =
|
|
40
|
-
fail ArgumentError, 'No :client specified' if c.nil?
|
|
41
|
-
|
|
42
|
-
p = opts[:project]
|
|
43
|
-
fail ArgumentError, 'No :project specified' if p.nil?
|
|
44
|
-
|
|
45
|
-
project = GoodData::Project[p, opts]
|
|
46
|
-
fail ArgumentError, 'Wrong :project specified' if project.nil?
|
|
33
|
+
c, project = GoodData.get_client_and_project(opts)
|
|
47
34
|
|
|
48
35
|
if id == :all
|
|
49
36
|
GoodData::Schedule.all(opts)
|
|
@@ -62,14 +49,7 @@ module GoodData
|
|
|
62
49
|
# Returns list of all schedules for active project
|
|
63
50
|
# @return [Array<GoodData::Schedule>] List of schedules
|
|
64
51
|
def all(opts = { :client => GoodData.connection, :project => GoodData.project })
|
|
65
|
-
c =
|
|
66
|
-
fail ArgumentError, 'No :client specified' if c.nil?
|
|
67
|
-
|
|
68
|
-
p = opts[:project]
|
|
69
|
-
fail ArgumentError, 'No :project specified' if p.nil?
|
|
70
|
-
|
|
71
|
-
project = GoodData::Project[p, opts]
|
|
72
|
-
fail ArgumentError, 'Wrong :project specified' if project.nil?
|
|
52
|
+
c, project = GoodData.get_client_and_project(opts)
|
|
73
53
|
|
|
74
54
|
tmp = c.get "/gdc/projects/#{project.pid}/schedules"
|
|
75
55
|
tmp['schedules']['items'].map { |schedule| c.create(GoodData::Schedule, schedule, project: project) }
|
|
@@ -83,20 +63,13 @@ module GoodData
|
|
|
83
63
|
# @param options [Hash] Optional options
|
|
84
64
|
# @return [GoodData::Schedule] New GoodData::Schedule instance
|
|
85
65
|
def create(process_id, trigger, executable, options = {})
|
|
86
|
-
c =
|
|
87
|
-
fail ArgumentError, 'No :client specified' if c.nil?
|
|
88
|
-
|
|
89
|
-
p = options[:project]
|
|
90
|
-
fail ArgumentError, 'No :project specified' if p.nil?
|
|
91
|
-
|
|
92
|
-
project = GoodData::Project[p, options]
|
|
93
|
-
fail ArgumentError, 'Wrong :project specified' if project.nil?
|
|
66
|
+
c, project = GoodData.get_client_and_project(options)
|
|
94
67
|
|
|
95
68
|
fail 'Process ID has to be provided' if process_id.blank?
|
|
96
69
|
fail 'Executable has to be provided' if executable.blank?
|
|
97
70
|
fail 'Trigger schedule has to be provided' if trigger.blank?
|
|
98
71
|
|
|
99
|
-
schedule = c.create(GoodData::Schedule, GoodData::Helpers.deep_stringify_keys(GoodData::Helpers.deep_dup(SCHEDULE_TEMPLATE)), client: c, project:
|
|
72
|
+
schedule = c.create(GoodData::Schedule, GoodData::Helpers.deep_stringify_keys(GoodData::Helpers.deep_dup(SCHEDULE_TEMPLATE)), client: c, project: project)
|
|
100
73
|
|
|
101
74
|
default_opts = {
|
|
102
75
|
:type => 'MSETL',
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
#
|
|
3
|
+
# Copyright (c) 2010-2015 GoodData Corporation. All rights reserved.
|
|
4
|
+
# This source code is licensed under the BSD-style license found in the
|
|
5
|
+
# LICENSE file in the root directory of this source tree.
|
|
6
|
+
|
|
7
|
+
require_relative './client'
|
|
8
|
+
require_relative './domain'
|
|
9
|
+
require_relative '../models/client_synchronization_result'
|
|
10
|
+
|
|
11
|
+
require_relative '../mixins/data_property_reader'
|
|
12
|
+
require_relative '../mixins/links'
|
|
13
|
+
require_relative '../mixins/rest_resource'
|
|
14
|
+
require_relative '../mixins/uri_getter'
|
|
15
|
+
|
|
16
|
+
module GoodData
|
|
17
|
+
class Segment < Rest::Resource
|
|
18
|
+
SYNCHRONIZE_URI = '/gdc/domains/%s/segments/%s/synchronizeClients'
|
|
19
|
+
|
|
20
|
+
attr_accessor :domain
|
|
21
|
+
|
|
22
|
+
data_property_reader 'id'
|
|
23
|
+
|
|
24
|
+
include Mixin::Links
|
|
25
|
+
include Mixin::UriGetter
|
|
26
|
+
|
|
27
|
+
SEGMENT_TEMPLATE = {
|
|
28
|
+
:segment => {
|
|
29
|
+
:id => nil,
|
|
30
|
+
:masterProject => nil
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
class << self
|
|
35
|
+
# Returns list of all segments or a particular segment
|
|
36
|
+
#
|
|
37
|
+
# @param id [String|Symbol] Uri of the segment required or :all for all segments.
|
|
38
|
+
# @return [Array<GoodData::Segment>] List of segments for a particular domain
|
|
39
|
+
def [](id, opts = {})
|
|
40
|
+
domain = opts[:domain]
|
|
41
|
+
fail ArgumentError, 'No :domain specified' if domain.nil?
|
|
42
|
+
|
|
43
|
+
client = domain.client
|
|
44
|
+
fail ArgumentError, 'No client specified' if client.nil?
|
|
45
|
+
|
|
46
|
+
if id == :all
|
|
47
|
+
GoodData::Segment.all(opts)
|
|
48
|
+
else
|
|
49
|
+
result = client.get(domain.segments_uri + "/segments/#{CGI.escape(id)}")
|
|
50
|
+
client.create(GoodData::Segment, result.merge('domain' => domain))
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Returns list of all segments for domain
|
|
55
|
+
#
|
|
56
|
+
# @param opts [Hash] Options. Should contain :domain for which you want to get the segments.
|
|
57
|
+
# @return [Array<GoodData::Segment>] List of segments for a particular domain
|
|
58
|
+
def all(opts = {})
|
|
59
|
+
domain = opts[:domain]
|
|
60
|
+
fail 'Domain has to be passed in options' unless domain
|
|
61
|
+
client = domain.client
|
|
62
|
+
|
|
63
|
+
results = client.get(domain.segments_uri + '/segments')
|
|
64
|
+
GoodData::Helpers.get_path(results, %w(segments items)).map { |i| client.create(GoodData::Segment, i.merge('domain' => domain)) }
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Creates new segment from parameters passed
|
|
68
|
+
#
|
|
69
|
+
# @param data [Hash] Data for segment namely :segment_id and :master_project is accepted. Master_project can be given as either a PID or a Project instance
|
|
70
|
+
# @param options [Hash] Trigger of schedule. Can be cron string or reference to another schedule.
|
|
71
|
+
# @return [GoodData::Segment] New Segment instance
|
|
72
|
+
def create(data = {}, options = {})
|
|
73
|
+
segment_id = data[:segment_id]
|
|
74
|
+
fail 'Custom ID has to be provided' if segment_id.blank?
|
|
75
|
+
client = options[:client]
|
|
76
|
+
segment = client.create(GoodData::Segment, GoodData::Helpers.deep_stringify_keys(SEGMENT_TEMPLATE).merge('domain' => options[:domain]))
|
|
77
|
+
segment.tap do |s|
|
|
78
|
+
s.segment_id = segment_id
|
|
79
|
+
s.master_project = data[:master_project]
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def initialize(data)
|
|
85
|
+
super
|
|
86
|
+
@domain = data.delete('domain')
|
|
87
|
+
@json = data
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Segment id getter for the Segment. Called segment_id since id is a reserved word in ruby world
|
|
91
|
+
#
|
|
92
|
+
# @return [String] Segment id
|
|
93
|
+
def segment_id
|
|
94
|
+
data['id']
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Segment id setter for the Segment. Called segment_id since id is a reserved word in ruby world
|
|
98
|
+
#
|
|
99
|
+
# @param an_id [String] Id of the segment.
|
|
100
|
+
# @return [String] Segment id
|
|
101
|
+
def segment_id=(an_id)
|
|
102
|
+
data['id'] = an_id
|
|
103
|
+
self
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Master project id getter for the Segment.
|
|
107
|
+
#
|
|
108
|
+
# @return [String] Segment id
|
|
109
|
+
def master_project=(a_project)
|
|
110
|
+
data['masterProject'] = a_project.respond_to?(:uri) ? a_project.uri : a_project
|
|
111
|
+
self
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
alias_method :master=, :master_project=
|
|
115
|
+
|
|
116
|
+
# Master project id getter for the Segment.
|
|
117
|
+
#
|
|
118
|
+
# @return [String] Project uri
|
|
119
|
+
def master_project_uri
|
|
120
|
+
data['masterProject']
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
alias_method :master_uri, :master_project_uri
|
|
124
|
+
|
|
125
|
+
# Master project getter for the Segment. It returns the instance not just the URI
|
|
126
|
+
#
|
|
127
|
+
# @return [GoodData::Project] Project associated with the segment
|
|
128
|
+
def master_project
|
|
129
|
+
client.projects(master_project_uri)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
alias_method :master, :master_project
|
|
133
|
+
|
|
134
|
+
def create_client(data)
|
|
135
|
+
client = GoodData::Client.create(data, segment: self)
|
|
136
|
+
client.save
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# Returns all the clients associated with the segment. Since this is potentially paging operation it returns an Enumerable.
|
|
140
|
+
#
|
|
141
|
+
# @return [Enumerable] Clients associated with the segment
|
|
142
|
+
def clients(tenant_id = :all)
|
|
143
|
+
GoodData::Client[tenant_id, domain: domain, segment: self]
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# Creates or updates a segment instance on the API.
|
|
147
|
+
#
|
|
148
|
+
# @return [GoodData::Segment] Segment instance
|
|
149
|
+
def save
|
|
150
|
+
if uri
|
|
151
|
+
client.put(uri, json)
|
|
152
|
+
else
|
|
153
|
+
res = client.post(domain.segments_uri + '/segments', json)
|
|
154
|
+
@json = res
|
|
155
|
+
end
|
|
156
|
+
self
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# Runs async process that walks thorugh segments and provisions projects if necessary.
|
|
160
|
+
#
|
|
161
|
+
# @return [Array] Returns array of results
|
|
162
|
+
def synchronize_clients
|
|
163
|
+
sync_uri = SYNCHRONIZE_URI % [domain.obj_id, id]
|
|
164
|
+
res = client.post sync_uri, nil
|
|
165
|
+
|
|
166
|
+
# wait until the instance is created
|
|
167
|
+
res = client.poll_on_response(res['asyncTask']['links']['poll'], :sleep_interval => 1) do |r|
|
|
168
|
+
r['synchronizationResult'].nil?
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
client.create(ClientSynchronizationResult, res)
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
# Deletes a segment instance on the API.
|
|
175
|
+
#
|
|
176
|
+
# @return [GoodData::Segment] Segment instance
|
|
177
|
+
def delete(options = {})
|
|
178
|
+
force = options[:force] == true ? true : false
|
|
179
|
+
clients.peach(&:delete) if force
|
|
180
|
+
client.delete(uri) if uri
|
|
181
|
+
self
|
|
182
|
+
rescue RestClient::BadRequest => e
|
|
183
|
+
payload = GoodData::Helpers.parse_http_exception(e)
|
|
184
|
+
case GoodData::Helpers.get_path(payload)
|
|
185
|
+
when 'gdc.c4.conflict.domain.segment.contains_clients'
|
|
186
|
+
throw SegmentNotEmpty
|
|
187
|
+
else
|
|
188
|
+
raise e
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
end
|
|
@@ -21,11 +21,11 @@ module GoodData
|
|
|
21
21
|
c = client(options)
|
|
22
22
|
project = options[:project]
|
|
23
23
|
filters = query('userFilter', nil, options)
|
|
24
|
-
count =
|
|
24
|
+
count = 1_000
|
|
25
25
|
offset = 0
|
|
26
26
|
user_lookup = {}
|
|
27
27
|
loop do
|
|
28
|
-
result = c.get("/gdc/md/#{project.pid}/userfilters?count
|
|
28
|
+
result = c.get("/gdc/md/#{project.pid}/userfilters?count=#{count}&offset=#{offset}")
|
|
29
29
|
result['userFilters']['items'].each do |item|
|
|
30
30
|
item['userFilters'].each do |f|
|
|
31
31
|
user_lookup[f] = item['user']
|
|
@@ -186,7 +186,7 @@ module GoodData
|
|
|
186
186
|
# so it precaches the values and still be able to function for larger ones even
|
|
187
187
|
# though that would mean tons of requests
|
|
188
188
|
def self.get_small_labels(labels_cache)
|
|
189
|
-
labels_cache.values.select { |label| label.values_count < 100_000 }
|
|
189
|
+
labels_cache.values.select { |label| label && label.values_count && label.values_count < 100_000 }
|
|
190
190
|
end
|
|
191
191
|
|
|
192
192
|
# Creates a MAQL expression(s) based on the filter defintion.
|
|
@@ -16,5 +16,16 @@ module GoodData
|
|
|
16
16
|
@json[:uri] = res['uri']
|
|
17
17
|
self
|
|
18
18
|
end
|
|
19
|
+
|
|
20
|
+
# Method used for replacing values in their state according to mapping. Can be used to replace any values but it is typically used to replace the URIs. Returns a new object of the same type.
|
|
21
|
+
#
|
|
22
|
+
# @param [Array<Array>]Mapping specifying what should be exchanged for what. As mapping should be used output of GoodData::Helpers.prepare_mapping.
|
|
23
|
+
# @return [GoodData::VariableUserFilter]
|
|
24
|
+
def replace(mapping)
|
|
25
|
+
x = GoodData::MdObject.replace_quoted(self, mapping)
|
|
26
|
+
x = GoodData::MdObject.replace_bracketed(x, mapping)
|
|
27
|
+
vals = GoodData::MdObject.find_replaceable_values(x, mapping)
|
|
28
|
+
GoodData::MdObject.replace_bracketed(x, vals)
|
|
29
|
+
end
|
|
19
30
|
end
|
|
20
31
|
end
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
#
|
|
3
|
+
# Copyright (c) 2010-2015 GoodData Corporation. All rights reserved.
|
|
4
|
+
# This source code is licensed under the BSD-style license found in the
|
|
5
|
+
# LICENSE file in the root directory of this source tree.
|
|
6
|
+
|
|
7
|
+
require_relative '../rest/rest'
|
|
8
|
+
require_relative '../rest/resource'
|
|
9
|
+
require_relative '../mixins/author'
|
|
10
|
+
require_relative '../mixins/contributor'
|
|
11
|
+
require_relative '../mixins/links'
|
|
12
|
+
require_relative '../mixins/rest_resource'
|
|
13
|
+
require_relative '../mixins/uri_getter'
|
|
14
|
+
|
|
15
|
+
module GoodData
|
|
16
|
+
# Representation of User Group
|
|
17
|
+
#
|
|
18
|
+
# Use user groups to manage user access to dashboards on the GoodData Portal.
|
|
19
|
+
# Create groups to more quickly manage permissions for users with
|
|
20
|
+
# the the same role or who need similar access to dashboards.
|
|
21
|
+
# Groups can be part of groups.
|
|
22
|
+
class UserGroup < Rest::Resource
|
|
23
|
+
include Mixin::Author
|
|
24
|
+
include Mixin::Contributor
|
|
25
|
+
include Mixin::Links
|
|
26
|
+
include Mixin::UriGetter
|
|
27
|
+
|
|
28
|
+
EMPTY_OBJECT = {
|
|
29
|
+
'userGroup' => {
|
|
30
|
+
'content' => {
|
|
31
|
+
'name' => nil,
|
|
32
|
+
'description' => nil,
|
|
33
|
+
'project' => nil
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
class << self
|
|
39
|
+
# Returns list of all segments or a particular segment
|
|
40
|
+
#
|
|
41
|
+
# @param id [String|Symbol] Uri of the segment required or :all for all segments.
|
|
42
|
+
# @return [Array<GoodData::Segment>] List of segments for a particular domain
|
|
43
|
+
def [](id, opts = {})
|
|
44
|
+
# TODO: Replace with GoodData.get_client_and_project(opts)
|
|
45
|
+
project = opts[:project]
|
|
46
|
+
fail 'Project has to be passed in options' unless project
|
|
47
|
+
fail 'Project has to be of type GoodData::Project' unless project.is_a?(GoodData::Project)
|
|
48
|
+
client = project.client
|
|
49
|
+
|
|
50
|
+
results = client.get('/gdc/userGroups', params: { :project => project.pid, :user => opts[:user] }.compact)
|
|
51
|
+
groups = GoodData::Helpers.get_path(results, %w(userGroups items)).map { |i| client.create(GoodData::UserGroup, i, :project => project) }
|
|
52
|
+
id == :all ? groups : groups.find { |g| g.obj_id == id || g.name == id }
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Create new user group
|
|
56
|
+
#
|
|
57
|
+
# @param data [Hash] Initial data
|
|
58
|
+
# @return [UserGroup] Newly created user group
|
|
59
|
+
def create(data)
|
|
60
|
+
new_data = GoodData::Helpers.deep_dup(EMPTY_OBJECT).tap do |d|
|
|
61
|
+
d['userGroup']['content']['name'] = data[:name]
|
|
62
|
+
d['userGroup']['content']['description'] = data[:description]
|
|
63
|
+
d['userGroup']['content']['project'] = data[:project].respond_to?(:uri) ? data[:project].uri : data[:project]
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
client.create(GoodData::UserGroup, GoodData::Helpers.deep_stringify_keys(new_data))
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Constructs payload for user management/manipulation
|
|
70
|
+
#
|
|
71
|
+
# @return [Hash] Created payload
|
|
72
|
+
def construct_payload(users, operation)
|
|
73
|
+
users = users.is_a?(Array) ? users : [users]
|
|
74
|
+
|
|
75
|
+
{
|
|
76
|
+
modifyMembers: {
|
|
77
|
+
operation: operation,
|
|
78
|
+
items: users.map do |user|
|
|
79
|
+
uri = user.respond_to?(:uri) ? user.uri : user
|
|
80
|
+
fail 'You cannot add group as member of another group as of now.' if uri =~ %r{^\/gdc\/userGroups\/}
|
|
81
|
+
uri
|
|
82
|
+
end
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# URI used for membership manipulation/managementv
|
|
88
|
+
#
|
|
89
|
+
# @param client [Client] Client used for communication with platform
|
|
90
|
+
# @param users [User | String | Array<User> | Array<String>] User(s) to be modified
|
|
91
|
+
# @param operation [String] Operation to be performed - ADD, SET, REMOVE
|
|
92
|
+
# @param uri [String] URI to be used for operation
|
|
93
|
+
# @return [String] URI used for membership manipulation/management
|
|
94
|
+
def modify_users(client, users, operation, uri)
|
|
95
|
+
payload = construct_payload(users, operation)
|
|
96
|
+
client.post(uri, payload)
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Initialize object with json
|
|
101
|
+
#
|
|
102
|
+
# @return [UserGroup] User Group object initialized with json
|
|
103
|
+
def initialize(json)
|
|
104
|
+
@json = json
|
|
105
|
+
self
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Add member(s) to user group
|
|
109
|
+
#
|
|
110
|
+
# @param [String | User | Array<User>] Users to add to user group
|
|
111
|
+
# @return [nil] Nothing is returned
|
|
112
|
+
def add_members(user)
|
|
113
|
+
UserGroup.modify_users(client, user, 'ADD', uri_modify_members)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
alias_method :add_member, :add_members
|
|
117
|
+
|
|
118
|
+
# Gets user group name
|
|
119
|
+
#
|
|
120
|
+
# @return [String] User group name
|
|
121
|
+
def name
|
|
122
|
+
content['name']
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Sets user group name
|
|
126
|
+
#
|
|
127
|
+
# @param name [String] New user group name
|
|
128
|
+
# @return [String] New user group name
|
|
129
|
+
def name=(name)
|
|
130
|
+
content['name'] = name
|
|
131
|
+
name
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Gets user group description
|
|
135
|
+
#
|
|
136
|
+
# @return [String] User group description
|
|
137
|
+
def description
|
|
138
|
+
content['description']
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# Sets user group description
|
|
142
|
+
#
|
|
143
|
+
# @param name [String] New user group description
|
|
144
|
+
# @return [String] New user group description
|
|
145
|
+
def description=(name)
|
|
146
|
+
content['description'] = name
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# Gets Users with this Role
|
|
150
|
+
#
|
|
151
|
+
# @return [Array<GoodData::Profile>] List of users
|
|
152
|
+
def members
|
|
153
|
+
url = GoodData::Helpers.get_path(data, %w(links members))
|
|
154
|
+
return [] unless url
|
|
155
|
+
Enumerator.new do |y|
|
|
156
|
+
loop do
|
|
157
|
+
res = client.get url
|
|
158
|
+
res['userGroupMembers']['paging']['next']
|
|
159
|
+
res['userGroupMembers']['items'].each do |member|
|
|
160
|
+
case member.keys.first
|
|
161
|
+
when 'user'
|
|
162
|
+
y << client.create(GoodData::Profile, client.get(GoodData::Helpers.get_path(member, %w(user links self))), :project => project)
|
|
163
|
+
when 'userGroup'
|
|
164
|
+
y << client.create(UserGroup, client.get(GoodData::Helpers.get_path(member, %w(userGroup links self))), :project => project)
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
url = res['userGroupMembers']['paging']['next']
|
|
168
|
+
break unless url
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
# Verifies if user is in a group or any nested group and returns true if it does
|
|
174
|
+
#
|
|
175
|
+
# @return [Boolean] Retruns true if member is member of the group or any of its members
|
|
176
|
+
def member?(a_member)
|
|
177
|
+
# could be better on API directly?
|
|
178
|
+
uri = a_member.respond_to?(:uri) ? a_member.uri : a_member
|
|
179
|
+
members.map(&:uri).include?(uri)
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
# Save user group
|
|
183
|
+
# New group is created if needed else existing one is updated
|
|
184
|
+
#
|
|
185
|
+
# @return [UserGroup] Created or updated user group
|
|
186
|
+
def save
|
|
187
|
+
res = if uri
|
|
188
|
+
# get rid of unsupprted keys
|
|
189
|
+
data = json['userGroup']
|
|
190
|
+
client.put(uri, 'userGroup' => data.except('meta', 'links'))
|
|
191
|
+
else
|
|
192
|
+
client.post('/gdc/userGroups', @json)
|
|
193
|
+
end
|
|
194
|
+
@json = client.get(res['uri'])
|
|
195
|
+
self
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
# Remove member(s) from user group
|
|
199
|
+
#
|
|
200
|
+
# @param [String | User | Array<User>] Users to remove from user group
|
|
201
|
+
# @return [nil] Nothing is returned
|
|
202
|
+
def remove_members(user)
|
|
203
|
+
UserGroup.modify_users(client, user, 'REMOVE', uri_modify_members)
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
alias_method :remove_member, :remove_members
|
|
207
|
+
|
|
208
|
+
# Set member(s) to user group.
|
|
209
|
+
# Only users passed to this call will be new members of user group.
|
|
210
|
+
# Old members not passed to this method will be removed!
|
|
211
|
+
#
|
|
212
|
+
# @param [String | User | Array<User>] Users to set as members of user group
|
|
213
|
+
# @return [nil] Nothing is returned
|
|
214
|
+
def set_members(user) # rubocop:disable Style/AccessorMethodName
|
|
215
|
+
UserGroup.modify_users(client, user, 'SET', uri_modify_members)
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
alias_method :set_member, :set_members
|
|
219
|
+
|
|
220
|
+
# URI used for membership manipulation/management
|
|
221
|
+
#
|
|
222
|
+
# @return [String] URI used for membership manipulation/management
|
|
223
|
+
def uri_modify_members
|
|
224
|
+
links['modifyMembers']
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
# Is it a user group?
|
|
228
|
+
#
|
|
229
|
+
# @return [Boolean] Return true if it is a user group
|
|
230
|
+
def user_group?
|
|
231
|
+
true
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
# Checks if two user groups are same
|
|
235
|
+
#
|
|
236
|
+
# @return [Boolean] Return true if the two groups are same
|
|
237
|
+
def ==(other)
|
|
238
|
+
uri == other.uri
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
end
|