gooddata 0.6.53 → 0.6.54

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.
Files changed (139) hide show
  1. checksums.yaml +5 -5
  2. data/.flayignore +6 -0
  3. data/.gitignore +1 -0
  4. data/.pronto.yml +3 -0
  5. data/.rspec +2 -0
  6. data/.rubocop.yml +4 -1
  7. data/CHANGELOG.md +18 -0
  8. data/CONTRIBUTING.md +14 -1
  9. data/DEPENDENCIES.md +324 -253
  10. data/Dockerfile.jruby +5 -7
  11. data/Dockerfile.ruby +8 -8
  12. data/Rakefile +24 -0
  13. data/ci.rake +47 -0
  14. data/docker-compose.yml +34 -0
  15. data/gooddata.gemspec +8 -2
  16. data/lib/gooddata/bricks/middleware/restforce_middleware.rb +0 -3
  17. data/lib/gooddata/helpers/data_helper.rb +10 -7
  18. data/lib/gooddata/helpers/global_helpers_params.rb +8 -3
  19. data/lib/gooddata/lcm/actions/apply_custom_maql.rb +2 -1
  20. data/lib/gooddata/lcm/actions/associate_clients.rb +10 -1
  21. data/lib/gooddata/lcm/actions/collect_client_projects.rb +78 -0
  22. data/lib/gooddata/lcm/actions/collect_clients.rb +20 -6
  23. data/lib/gooddata/lcm/actions/collect_data_product.rb +62 -0
  24. data/lib/gooddata/lcm/actions/collect_dynamic_schedule_params.rb +62 -0
  25. data/lib/gooddata/lcm/actions/{collect_attrs.rb → collect_ldm_objects.rb} +3 -3
  26. data/lib/gooddata/lcm/actions/collect_meta.rb +6 -3
  27. data/lib/gooddata/lcm/actions/collect_segment_clients.rb +2 -1
  28. data/lib/gooddata/lcm/actions/collect_segments.rb +6 -7
  29. data/lib/gooddata/lcm/actions/collect_tagged_objects.rb +7 -4
  30. data/lib/gooddata/lcm/actions/create_segment_masters.rb +7 -3
  31. data/lib/gooddata/lcm/actions/ensure_data_product.rb +53 -0
  32. data/lib/gooddata/lcm/actions/ensure_technical_users_domain.rb +6 -2
  33. data/lib/gooddata/lcm/actions/ensure_technical_users_project.rb +30 -18
  34. data/lib/gooddata/lcm/actions/execute_schedules.rb +128 -0
  35. data/lib/gooddata/lcm/actions/provision_clients.rb +32 -21
  36. data/lib/gooddata/lcm/actions/purge_clients.rb +25 -39
  37. data/lib/gooddata/lcm/actions/rename_existing_client_projects.rb +70 -0
  38. data/lib/gooddata/lcm/actions/segments_filter.rb +6 -0
  39. data/lib/gooddata/lcm/actions/synchronize_cas.rb +11 -0
  40. data/lib/gooddata/lcm/actions/synchronize_clients.rb +2 -1
  41. data/lib/gooddata/lcm/actions/synchronize_etls_in_segment.rb +34 -15
  42. data/lib/gooddata/lcm/actions/synchronize_ldm.rb +10 -1
  43. data/lib/gooddata/lcm/actions/synchronize_new_segments.rb +2 -1
  44. data/lib/gooddata/lcm/actions/synchronize_processes.rb +4 -7
  45. data/lib/gooddata/lcm/actions/synchronize_tag_objects.rb +8 -5
  46. data/lib/gooddata/lcm/actions/synchronize_user_filters.rb +224 -0
  47. data/lib/gooddata/lcm/actions/synchronize_user_groups.rb +53 -0
  48. data/lib/gooddata/lcm/actions/synchronize_users.rb +324 -0
  49. data/lib/gooddata/lcm/dsl/type_dsl.rb +1 -0
  50. data/lib/gooddata/lcm/helpers/check_helper.rb +4 -0
  51. data/lib/gooddata/lcm/helpers/tags_helper.rb +4 -3
  52. data/lib/gooddata/lcm/lcm2.rb +33 -1
  53. data/lib/gooddata/lcm/types/complex/segment.rb +3 -0
  54. data/lib/gooddata/lcm/types/complex/update_preference.rb +8 -2
  55. data/lib/gooddata/lcm/types/special/array.rb +1 -3
  56. data/lib/gooddata/lcm/types/special/enum.rb +1 -3
  57. data/lib/gooddata/mixins/md_id_to_uri.rb +0 -1
  58. data/lib/gooddata/mixins/md_json.rb +2 -2
  59. data/lib/gooddata/models/blueprint/project_blueprint.rb +15 -0
  60. data/lib/gooddata/models/blueprint/to_wire.rb +1 -0
  61. data/lib/gooddata/models/client.rb +21 -9
  62. data/lib/gooddata/models/data_product.rb +149 -0
  63. data/lib/gooddata/models/domain.rb +26 -72
  64. data/lib/gooddata/models/from_wire.rb +2 -0
  65. data/lib/gooddata/models/metadata/report.rb +9 -3
  66. data/lib/gooddata/models/metadata/report_definition.rb +2 -2
  67. data/lib/gooddata/models/model.rb +1 -1
  68. data/lib/gooddata/models/process.rb +4 -0
  69. data/lib/gooddata/models/project.rb +58 -35
  70. data/lib/gooddata/models/project_creator.rb +13 -0
  71. data/lib/gooddata/models/segment.rb +63 -16
  72. data/lib/gooddata/models/style_setting.rb +2 -15
  73. data/lib/gooddata/models/user_group.rb +2 -0
  74. data/lib/gooddata/rest/connection.rb +32 -9
  75. data/lib/gooddata/rest/object_factory.rb +0 -25
  76. data/lib/gooddata/version.rb +1 -1
  77. data/spec/data/blueprints/invalid_blueprint.json +2 -2
  78. data/spec/data/blueprints/test_project_model_spec.json +1 -1
  79. data/spec/data/dynamic_schedule_params_table.csv +7 -0
  80. data/spec/data/workspace_table.csv +3 -3
  81. data/spec/environment/staging.rb +3 -3
  82. data/spec/integration/ads_output_stage_spec.rb +0 -10
  83. data/spec/integration/clients_spec.rb +1 -1
  84. data/spec/{unit → integration}/commands/command_projects_spec.rb +0 -0
  85. data/spec/{unit → integration}/core/connection_spec.rb +0 -0
  86. data/spec/{unit → integration}/core/logging_spec.rb +0 -0
  87. data/spec/{unit → integration}/core/project_spec.rb +0 -0
  88. data/spec/integration/date_dim_switch_spec.rb +13 -0
  89. data/spec/integration/full_process_schedule_spec.rb +2 -2
  90. data/spec/integration/helpers_spec.rb +16 -0
  91. data/spec/integration/lcm_spec.rb +12 -2
  92. data/spec/integration/mixins/id_to_uri_spec.rb +44 -0
  93. data/spec/integration/models/data_product_spec.rb +71 -0
  94. data/spec/{unit → integration}/models/domain_spec.rb +2 -2
  95. data/spec/{unit → integration}/models/invitation_spec.rb +0 -0
  96. data/spec/{unit → integration}/models/membership_spec.rb +0 -0
  97. data/spec/{unit → integration}/models/params_spec.rb +0 -0
  98. data/spec/{unit → integration}/models/profile_spec.rb +0 -0
  99. data/spec/{unit → integration}/models/project_role_spec.rb +0 -0
  100. data/spec/integration/models/project_spec.rb +225 -0
  101. data/spec/{unit → integration}/models/schedule_spec.rb +0 -0
  102. data/spec/{unit → integration}/models/unit_project_spec.rb +0 -0
  103. data/spec/integration/project_spec.rb +40 -5
  104. data/spec/integration/segments_spec.rb +27 -26
  105. data/spec/integration/user_filters_spec.rb +1 -1
  106. data/spec/spec_helper.rb +15 -19
  107. data/spec/unit/actions/associate_clients_spec.rb +47 -0
  108. data/spec/unit/actions/collect_client_projects_spec.rb +47 -0
  109. data/spec/unit/actions/collect_clients_spec.rb +27 -0
  110. data/spec/unit/actions/collect_data_product_spec.rb +64 -0
  111. data/spec/unit/actions/collect_dynamic_schedule_params_spec.rb +56 -0
  112. data/spec/unit/actions/collect_meta_spec.rb +4 -4
  113. data/spec/unit/actions/collect_segment_clients_spec.rb +44 -3
  114. data/spec/unit/actions/collect_tagged_objects_spec.rb +20 -4
  115. data/spec/unit/actions/create_segment_masters_spec.rb +64 -0
  116. data/spec/unit/actions/ensure_data_product_spec.rb +38 -0
  117. data/spec/unit/actions/ensure_technical_users_domain_spec.rb +51 -0
  118. data/spec/unit/actions/ensure_technical_users_project_spec.rb +72 -0
  119. data/spec/unit/actions/execute_schedules_spec.rb +94 -0
  120. data/spec/unit/actions/provision_clients_spec.rb +45 -0
  121. data/spec/unit/actions/purge_clients_spec.rb +47 -0
  122. data/spec/unit/actions/rename_existing_client_projects_spec.rb +54 -0
  123. data/spec/unit/actions/segments_filter_spec.rb +46 -0
  124. data/spec/unit/actions/shared_examples_for_user_actions.rb +10 -0
  125. data/spec/unit/actions/synchronize_cas_spec.rb +58 -0
  126. data/spec/unit/actions/synchronize_etls_in_segment_spec.rb +174 -13
  127. data/spec/unit/actions/synchronize_ldm_spec.rb +57 -0
  128. data/spec/unit/actions/synchronize_user_filters_spec.rb +142 -0
  129. data/spec/unit/actions/synchronize_user_groups_spec.rb +49 -0
  130. data/spec/unit/actions/synchronize_users_spec.rb +76 -0
  131. data/spec/unit/helpers/data_helper_spec.rb +17 -0
  132. data/spec/unit/helpers/global_helpers_spec.rb +16 -0
  133. data/spec/unit/helpers_spec.rb +0 -6
  134. data/spec/unit/models/blueprint/project_blueprint_spec.rb +21 -4
  135. data/spec/unit/models/project_creator_spec.rb +16 -0
  136. data/spec/unit/models/project_spec.rb +66 -197
  137. metadata +202 -100
  138. data/PULL_REQUEST_TEMPLATE.md +0 -5
  139. data/lib/gooddata/bricks/middleware/params_inspect_middleware.rb +0 -21
@@ -6,6 +6,7 @@
6
6
  # LICENSE file in the root directory of this source tree.
7
7
 
8
8
  require_relative '../types/param'
9
+ require_relative '../types/special/types'
9
10
 
10
11
  module GoodData
11
12
  module LCM2
@@ -25,6 +25,10 @@ module GoodData
25
25
  fail "Expected parameter '#{param_name}' to be kind of '#{type}', got '#{value.class.name}'"
26
26
  end
27
27
 
28
+ if specification[param_name][:opts][:deprecated]
29
+ puts "WARNING: Parameter '#{param_name}' is deprecated. Please use '#{specification[param_name][:opts][:replacement]}' instead."
30
+ end
31
+
28
32
  unless type.check(value)
29
33
  fail "Parameter '#{param_name}' has invalid type, expected: #{type}"
30
34
  end
@@ -14,8 +14,8 @@ module GoodData
14
14
  def segment_production_tags(segments)
15
15
  return {} unless segments
16
16
  segments
17
- .reject { |s| s.production_tag.nil? }
18
- .map { |s| [s.segment_id, s.production_tag] }
17
+ .reject { |s| s.production_tags.nil? }
18
+ .map { |s| [s.segment_id, s.production_tags] }
19
19
  .to_h
20
20
  end
21
21
 
@@ -27,7 +27,8 @@ module GoodData
27
27
  separator = ','
28
28
  tags = segment_production_tags || production_tags
29
29
  return [] unless tags
30
- tags.split(separator).map(&:strip)
30
+ tags = tags.split(separator).map(&:strip) unless tags.is_a?(Array)
31
+ tags
31
32
  end
32
33
  end
33
34
  end
@@ -80,12 +80,14 @@ module GoodData
80
80
 
81
81
  release: [
82
82
  EnsureReleaseTable,
83
+ EnsureDataProduct,
84
+ CollectDataProduct,
83
85
  SegmentsFilter,
84
86
  CreateSegmentMasters,
85
87
  EnsureTechnicalUsersDomain,
86
88
  EnsureTechnicalUsersProject,
87
89
  SynchronizeLdm,
88
- CollectAttributes,
90
+ CollectLdmObjects,
89
91
  CollectMeta,
90
92
  CollectTaggedObjects,
91
93
  CollectComputedAttributeMetrics,
@@ -94,24 +96,30 @@ module GoodData
94
96
  SynchronizeProcesses,
95
97
  SynchronizeSchedules,
96
98
  SynchronizeColorPalette,
99
+ SynchronizeUserGroups,
97
100
  SynchronizeNewSegments,
98
101
  UpdateReleaseTable
99
102
  ],
100
103
 
101
104
  provision: [
102
105
  EnsureReleaseTable,
106
+ CollectDataProduct,
103
107
  CollectSegments,
108
+ CollectClientProjects,
104
109
  PurgeClients,
105
110
  CollectClients,
106
111
  AssociateClients,
112
+ RenameExistingClientProjects,
107
113
  ProvisionClients,
108
114
  EnsureTechnicalUsersDomain,
109
115
  EnsureTechnicalUsersProject,
116
+ CollectDymanicScheduleParams,
110
117
  SynchronizeETLsInSegment
111
118
  ],
112
119
 
113
120
  rollout: [
114
121
  EnsureReleaseTable,
122
+ CollectDataProduct,
115
123
  CollectSegments,
116
124
  CollectSegmentClients,
117
125
  EnsureTechnicalUsersDomain,
@@ -120,7 +128,23 @@ module GoodData
120
128
  ApplyCustomMaql,
121
129
  SynchronizeClients,
122
130
  SynchronizeComputedAttributes,
131
+ CollectDymanicScheduleParams,
123
132
  SynchronizeETLsInSegment
133
+ ],
134
+
135
+ users: [
136
+ CollectDataProduct,
137
+ CollectSegments,
138
+ SynchronizeUsers
139
+ ],
140
+
141
+ user_filters: [
142
+ CollectDataProduct,
143
+ SynchronizeUserFilters
144
+ ],
145
+
146
+ schedules_execution: [
147
+ ExecuteSchedules
124
148
  ]
125
149
  }
126
150
 
@@ -130,6 +154,12 @@ module GoodData
130
154
  def convert_params(params)
131
155
  # Symbolize all keys
132
156
  GoodData::Helpers.symbolize_keys!(params)
157
+ params.keys.each do |k|
158
+ params[k.downcase] = params[k]
159
+ end
160
+ params.reject! do |k, _|
161
+ k.downcase != k
162
+ end
133
163
  convert_to_smart_hash(params)
134
164
  end
135
165
 
@@ -278,6 +308,8 @@ module GoodData
278
308
 
279
309
  # Invoke action
280
310
  begin
311
+ GoodData.logger.info("Running #{action.name} action ...")
312
+
281
313
  # Check if all required parameters were passed
282
314
  BaseAction.check_params(action.const_get('PARAMS'), params)
283
315
 
@@ -26,6 +26,9 @@ module GoodData
26
26
 
27
27
  description 'Master Project Name'
28
28
  param :master_name, instance_of(Type::StringType), required: true
29
+
30
+ description 'Production Tag Names'
31
+ param :production_tags, array_of(instance_of(Type::StringType)), required: false
29
32
  end
30
33
 
31
34
  def check(value)
@@ -17,10 +17,16 @@ module GoodData
17
17
 
18
18
  PARAMS = define_type(self) do
19
19
  description 'Cascade Drop'
20
- param :cascade_drops, instance_of(Type::BooleanType), required: false, default: nil
20
+ param :cascade_drops, instance_of(Type::BooleanType), required: false, default: nil, deprecated: true, replacement: :allow_cascade_drops
21
21
 
22
22
  description 'Preserve Data'
23
- param :preserve_data, instance_of(Type::BooleanType), required: false, default: nil
23
+ param :preserve_data, instance_of(Type::BooleanType), required: false, default: nil, deprecated: true, replacement: :keep_data
24
+
25
+ description 'Allow Cascade Drop'
26
+ param :allow_cascade_drops, instance_of(Type::BooleanType), required: false, default: false
27
+
28
+ description 'Keep Data'
29
+ param :keep_data, instance_of(Type::BooleanType), required: false, default: true
24
30
  end
25
31
 
26
32
  def check(value)
@@ -4,12 +4,10 @@
4
4
  # This source code is licensed under the BSD-style license found in the
5
5
  # LICENSE file in the root directory of this source tree.
6
6
 
7
- require_relative '../base_type'
8
-
9
7
  module GoodData
10
8
  module LCM2
11
9
  module Type
12
- class ArrayType < BaseType
10
+ class ArrayType
13
11
  CATEGORY = :special
14
12
 
15
13
  def initialize(type)
@@ -4,12 +4,10 @@
4
4
  # This source code is licensed under the BSD-style license found in the
5
5
  # LICENSE file in the root directory of this source tree.
6
6
 
7
- require_relative '../base_type'
8
-
9
7
  module GoodData
10
8
  module LCM2
11
9
  module Type
12
- class EnumType < BaseType
10
+ class EnumType
13
11
  CATEGORY = :special
14
12
  end
15
13
  end
@@ -9,7 +9,6 @@ module GoodData
9
9
  module MdIdToUri
10
10
  IDENTIFIERS_CFG = 'instance-identifiers'
11
11
 
12
- # TODO: Add test
13
12
  def identifier_to_uri(opts = { :client => GoodData.connection, :project => GoodData.project }, *ids)
14
13
  client, project = GoodData.get_client_and_project(opts)
15
14
 
@@ -7,8 +7,8 @@
7
7
  module GoodData
8
8
  module Mixin
9
9
  module MdJson
10
- def to_json
11
- json.to_json
10
+ def to_json(*args)
11
+ json.to_json(*args)
12
12
  end
13
13
  end
14
14
  end
@@ -742,11 +742,26 @@ module GoodData
742
742
  stuff.map { |r| { type: :bad_reference, reference: r.data, referencing_dataset: r.data[:dataset] } }
743
743
  end
744
744
 
745
+ # Validate the blueprint in particular if all ids must be unique and valid.
746
+ #
747
+ # @return [Array] array of errors
748
+ def validate_identifiers
749
+ ids = datasets.flat_map { |dataset| dataset.columns.map(&:id) }.compact
750
+
751
+ duplicated_ids = ids.select { |id| ids.count(id) > 1 }.uniq
752
+ errors = duplicated_ids.map { |id| { type: :duplicated_identifier, id: id } } || []
753
+
754
+ ids.uniq.each { |id| errors << { type: :invalid_identifier, id: id } if id !~ /^[\w.]+$/ }
755
+
756
+ errors
757
+ end
758
+
745
759
  # Validate the blueprint and all its datasets return array of errors that are found.
746
760
  #
747
761
  # @return [Array] array of errors
748
762
  def validate
749
763
  errors = []
764
+ errors.concat validate_identifiers
750
765
  errors.concat validate_references
751
766
  errors.concat datasets.reduce([]) { |acc, elem| acc.concat(elem.validate) }
752
767
  errors.concat datasets.reduce([]) { |acc, elem| acc.concat(elem.validate_gd_data_type_errors) }
@@ -113,6 +113,7 @@ module GoodData
113
113
  dd[:name] = dataset[:id]
114
114
  dd[:urn] = dataset[:urn] if dataset[:urn]
115
115
  dd[:title] = GoodData::Model.title(dataset)
116
+ dd[:identifierPrefix] = dataset[:identifier_prefix] if dataset[:identifier_prefix]
116
117
  end
117
118
  { dateDimension: payload }
118
119
  end
@@ -33,13 +33,13 @@ module GoodData
33
33
  domain = opts[:domain]
34
34
  segment = opts[:segment]
35
35
  fail ArgumentError, 'No :domain specified' if domain.nil?
36
- fail ArgumentError, 'No :segment specified' if domain.nil?
37
-
38
36
  client = domain.client
39
37
  fail ArgumentError, 'No client specified' if client.nil?
38
+ data_product = opts[:data_product] || (segment ? segment.data_product : nil)
40
39
 
41
40
  if id == :all
42
- tenants_uri = domain.segments_uri + "/clients?segment=#{CGI.escape(segment.segment_id)}"
41
+ tenants_uri = base_uri(domain, data_product)
42
+ tenants_uri += "?segment=#{CGI.escape(segment.segment_id)}" if segment
43
43
  Enumerator.new do |y|
44
44
  loop do
45
45
  res = client.get tenants_uri
@@ -56,7 +56,8 @@ module GoodData
56
56
  end
57
57
  else
58
58
  id = id.respond_to?(:client_id) ? id.client_id : id
59
- data = client.get(domain.segments_uri + "/clients/#{CGI.escape(id)}")
59
+ tenant_uri = base_uri(domain, data_product)
60
+ data = client.get(tenant_uri + "/#{CGI.escape(id)}")
60
61
  client.create(GoodData::Client, data.merge('domain' => domain))
61
62
  end
62
63
  end
@@ -88,17 +89,27 @@ module GoodData
88
89
  return nil unless value
89
90
  domain = opts[:domain]
90
91
  client_id = opts[:client_id]
91
- uri = "#{domain.segments_uri}/clients/#{client_id}/settings/#{name}"
92
+ data_product_id = opts[:data_product_id]
93
+ uri = data_product_id ? GoodData::DataProduct::ONE_DATA_PRODUCT_PATH % { domain_name: domain.name, id: data_product_id } : domain.segments_uri
92
94
  body = {
93
95
  setting: {
94
96
  name: "#{name}",
95
97
  value: "#{value}"
96
98
  }
97
99
  }
98
- domain.client.put(uri, body)
100
+ domain.client.put(uri + "/clients/#{client_id}/settings/#{name}", body)
99
101
  nil
100
102
  end
101
103
  alias_method :add_setting, :update_setting
104
+
105
+ def base_uri(domain, data_product)
106
+ if data_product
107
+ uri = GoodData::DataProduct::ONE_DATA_PRODUCT_PATH % { domain_name: domain.name, id: data_product.data_product_id }
108
+ else
109
+ uri = domain.segments_uri
110
+ end
111
+ uri + '/clients'
112
+ end
102
113
  end
103
114
 
104
115
  def initialize(data)
@@ -189,7 +200,8 @@ module GoodData
189
200
  if uri
190
201
  client.put(uri, json)
191
202
  else
192
- res = client.post(domain.segments_uri + '/clients', json)
203
+ data_product = segment.data_product
204
+ res = client.post(self.class.base_uri(domain, data_product), json)
193
205
  @json = res
194
206
  end
195
207
  self
@@ -209,8 +221,8 @@ module GoodData
209
221
  end
210
222
 
211
223
  def settings
212
- uri = "#{domain.segments_uri}/clients/#{client_id}/settings"
213
- res = client.get(uri)
224
+ data_product = segment.data_product
225
+ res = client.get(self.class.base_uri(domain, data_product) + "/#{client_id}/settings")
214
226
  settings = GoodData::Helpers.get_path(res, %w(settingsList items))
215
227
  settings.map do |setting|
216
228
  setting = setting['setting']
@@ -0,0 +1,149 @@
1
+ # encoding: UTF-8
2
+ #
3
+ # Copyright (c) 2010-2017 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/resource'
8
+
9
+ module GoodData
10
+ class DataProduct < Rest::Resource
11
+ include Mixin::UriGetter
12
+
13
+ attr_accessor :domain
14
+
15
+ ALL_DATA_PRODUCTS_PATH = '/gdc/domains/%{domain_name}/dataproducts'
16
+ ONE_DATA_PRODUCT_PATH = '/gdc/domains/%{domain_name}/dataproducts/%{id}'
17
+
18
+ class << self
19
+ def [](id, opts)
20
+ domain = opts[:domain]
21
+ fail ArgumentError, 'No :domain specified' if domain.nil?
22
+
23
+ client = domain.client
24
+ fail ArgumentError, 'No client specified' if client.nil?
25
+
26
+ if id == :all
27
+ GoodData::DataProduct.all(opts)
28
+ else
29
+ data_products_uri = ONE_DATA_PRODUCT_PATH % { domain_name: domain.name, id: id }
30
+
31
+ result = client.get(data_products_uri)
32
+ client.create(GoodData::DataProduct, result.merge('domain' => domain))
33
+ end
34
+ end
35
+
36
+ def all(opts = {})
37
+ domain = opts[:domain]
38
+ fail ArgumentError, 'No :domain specified' if domain.nil?
39
+
40
+ client = domain.client
41
+ fail ArgumentError, 'No client specified' if client.nil?
42
+
43
+ data_products_uri = ALL_DATA_PRODUCTS_PATH % { domain_name: domain.name }
44
+
45
+ GoodData::Helpers.get_path(client.get(data_products_uri), %w(dataProducts items)).map do |i|
46
+ client.create(GoodData::DataProduct, i, domain: domain)
47
+ end
48
+ end
49
+
50
+ def create(data = {}, options = {})
51
+ fail 'id for data_product has to be provided' if data[:id].blank?
52
+ client = options[:client]
53
+ client.create(GoodData::DataProduct, GoodData::Helpers.stringify_keys(dataProduct: data), domain: options[:domain])
54
+ end
55
+ end
56
+
57
+ def initialize(json)
58
+ super
59
+ @json = json
60
+ @domain = json.delete('domain')
61
+ end
62
+
63
+ def clients
64
+ json = client.get(data['links']['clients'])
65
+
66
+ json['clients']['items'].map do |val|
67
+ client.create(GoodData::Client, val, domain: domain)
68
+ end
69
+ end
70
+
71
+ def segments
72
+ json = client.get(data['links']['segments'])
73
+
74
+ json['segments']['items'].map do |val|
75
+ client.create(GoodData::Segment, val, domain: domain, data_product: self)
76
+ end
77
+ end
78
+
79
+ def save
80
+ if @json[:uri]
81
+ client.put(uri, json)
82
+ else
83
+ data_products_uri = ALL_DATA_PRODUCTS_PATH % { domain_name: domain.name }
84
+ res = client.post(data_products_uri, json)
85
+ @json = res
86
+ end
87
+ self
88
+ end
89
+
90
+ def delete(options = {})
91
+ segments.peach { |s| s.delete(options) }
92
+ client.delete(uri) if uri
93
+ self
94
+ end
95
+
96
+ def update_clients(data, options = {})
97
+ if options[:delete_extra] && options[:delete_extra_in_segments]
98
+ fail 'Options delete_extra and delete_extra_in_segments are mutually exclusive.'
99
+ end
100
+
101
+ data_products_uri = ONE_DATA_PRODUCT_PATH % { domain_name: domain.name, id: data_product_id }
102
+
103
+ payload = data.map do |datum|
104
+ {
105
+ :client => {
106
+ :id => datum[:id],
107
+ :segment => data_products_uri + '/segments/' + datum[:segment]
108
+ }
109
+ }.tap do |h|
110
+ h[:client][:project] = datum[:project] if datum.key?(:project)
111
+ end
112
+ end
113
+
114
+ if options[:delete_extra]
115
+ res = client.post(data_products_uri + '/updateClients?deleteExtra=true', updateClients: { items: payload })
116
+ elsif options[:delete_extra_in_segments]
117
+ segments_to_delete_in = options[:delete_extra_in_segments]
118
+ .map { |segment| CGI.escape(segment) }
119
+ .join(',')
120
+ uri = data_products_uri + "/updateClients?deleteExtraInSegments=#{segments_to_delete_in}"
121
+ res = client.post(uri, updateClients: { items: payload })
122
+ else
123
+ res = client.post(data_products_uri + '/updateClients', updateClients: { items: payload })
124
+ end
125
+ data = GoodData::Helpers.get_path(res, ['updateClientsResponse'])
126
+ if data
127
+ result = data.flat_map { |k, v| v.map { |h| GoodData::Helpers.symbolize_keys(h.merge('type' => k)) } }
128
+ result.select { |r| r[:status] == 'DELETED' }.peach { |r| r[:originalProject] && client.delete(r[:originalProject]) } if options[:delete_projects]
129
+ result
130
+ else
131
+ []
132
+ end
133
+ end
134
+
135
+ def data_product_id
136
+ data['id']
137
+ end
138
+
139
+ def data_product_id=(new_id)
140
+ data['id'] = new_id
141
+ end
142
+
143
+ def create_segment(data)
144
+ segment = GoodData::Segment.create(data, domain: domain, client: domain.client)
145
+ segment.data_product = self
146
+ segment.save
147
+ end
148
+ end
149
+ end