gooddata 2.1.12 → 2.1.19

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/.gdc-ii-config.yaml +1 -1
  3. data/.sonar.settings +4 -0
  4. data/.travis.yml +78 -12
  5. data/CHANGELOG.md +37 -0
  6. data/Dockerfile +9 -3
  7. data/Dockerfile.jruby +11 -1
  8. data/LICENSE +30 -22
  9. data/LICENSE.rb +1 -1
  10. data/README.md +19 -1
  11. data/Rakefile +8 -1
  12. data/SDK_VERSION +1 -1
  13. data/VERSION +1 -1
  14. data/bin/test_projects_cleanup.rb +45 -3
  15. data/ci/postgresql/pom.xml +57 -0
  16. data/dev-gooddata-sso.pub.encrypted +40 -40
  17. data/docker-compose.lcm.yml +3 -0
  18. data/gdc_fossa_ruby_sdk.yaml +1 -0
  19. data/gooddata.gemspec +8 -5
  20. data/k8s/charts/lcm-bricks/Chart.yaml +1 -1
  21. data/k8s/charts/lcm-bricks/templates/prometheus/alertingRules.yaml +11 -1
  22. data/lcm.rake +10 -4
  23. data/lib/gooddata.rb +2 -0
  24. data/lib/gooddata/cloud_resources/{cloud_resouce_factory.rb → cloud_resource_factory.rb} +8 -0
  25. data/lib/gooddata/cloud_resources/cloud_resources.rb +1 -1
  26. data/lib/gooddata/cloud_resources/postgresql/drivers/.gitkeepme +0 -0
  27. data/lib/gooddata/cloud_resources/postgresql/postgresql_client.rb +107 -0
  28. data/lib/gooddata/commands/scaffold.rb +9 -10
  29. data/lib/gooddata/core/nil_logger.rb +3 -1
  30. data/lib/gooddata/helpers/data_helper.rb +1 -2
  31. data/lib/gooddata/helpers/global_helpers.rb +6 -5
  32. data/lib/gooddata/helpers/global_helpers_params.rb +2 -2
  33. data/lib/gooddata/lcm/actions/associate_clients.rb +8 -2
  34. data/lib/gooddata/lcm/actions/base_action.rb +0 -2
  35. data/lib/gooddata/lcm/actions/collect_tagged_objects.rb +2 -1
  36. data/lib/gooddata/lcm/actions/migrate_gdc_date_dimension.rb +116 -0
  37. data/lib/gooddata/lcm/actions/provision_clients.rb +31 -10
  38. data/lib/gooddata/lcm/actions/synchronize_ldm.rb +3 -1
  39. data/lib/gooddata/lcm/actions/synchronize_user_filters.rb +4 -0
  40. data/lib/gooddata/lcm/actions/synchronize_users.rb +7 -6
  41. data/lib/gooddata/lcm/lcm2.rb +1 -2
  42. data/lib/gooddata/lcm/types/base_type.rb +0 -2
  43. data/lib/gooddata/mixins/md_object_query.rb +8 -6
  44. data/lib/gooddata/models/blueprint/project_blueprint.rb +0 -2
  45. data/lib/gooddata/models/client.rb +14 -12
  46. data/lib/gooddata/models/data_source.rb +664 -0
  47. data/lib/gooddata/models/domain.rb +3 -2
  48. data/lib/gooddata/models/from_wire.rb +1 -0
  49. data/lib/gooddata/models/metadata/analytical_dashboard.rb +49 -0
  50. data/lib/gooddata/models/metadata/analytical_visualization_object.rb +30 -0
  51. data/lib/gooddata/models/metadata/scheduled_mail.rb +1 -1
  52. data/lib/gooddata/models/metadata/visualization_object.rb +50 -0
  53. data/lib/gooddata/models/process.rb +11 -3
  54. data/lib/gooddata/models/project.rb +104 -13
  55. data/lib/gooddata/models/user_filters/user_filter_builder.rb +0 -1
  56. data/lib/gooddata/models/user_group.rb +0 -1
  57. data/lib/gooddata/rest/connection.rb +6 -4
  58. data/lib/gooddata/rest/phmap.rb +1 -1
  59. data/rubydev_public.gpg.encrypted +51 -51
  60. data/rubydev_secret_keys.gpg.encrypted +109 -109
  61. metadata +21 -22
  62. data/DEPENDENCIES.md +0 -880
@@ -1,4 +1,5 @@
1
- # Copyright (c) 2010-2017 GoodData Corporation. All rights reserved.
1
+ # frozen_string_literal: true
2
+ # Copyright (c) 2010-2021 GoodData Corporation. All rights reserved.
2
3
  # This source code is licensed under the BSD-style license found in the
3
4
  # LICENSE file in the root directory of this source tree.
4
5
 
@@ -11,6 +12,7 @@ module GoodData
11
12
  @level = nil
12
13
  end
13
14
 
15
+ # No body define need for dummy logger
14
16
  def debug(*_args)
15
17
  end
16
18
 
@@ -44,9 +44,8 @@ module GoodData
44
44
  realize_link
45
45
  when 's3'
46
46
  realize_s3(params)
47
- when 'redshift', 'snowflake', 'bigquery'
47
+ when 'redshift', 'snowflake', 'bigquery', 'postgresql'
48
48
  raise GoodData::InvalidEnvError, "DataSource does not support type \"#{source}\" on the platform #{RUBY_PLATFORM}" unless RUBY_PLATFORM =~ /java/
49
-
50
49
  require_relative '../cloud_resources/cloud_resources'
51
50
  realize_cloud_resource(source, params)
52
51
  else
@@ -1,6 +1,6 @@
1
1
  # encoding: UTF-8
2
2
  #
3
- # Copyright (c) 2010-2017 GoodData Corporation. All rights reserved.
3
+ # Copyright (c) 2010-2021 GoodData Corporation. All rights reserved.
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
 
@@ -34,6 +34,7 @@ module GoodData
34
34
  end
35
35
 
36
36
  set_const :GD_MAX_RETRY, (ENV['GD_MAX_RETRY'] && ENV['GD_MAX_RETRY'].to_i) || 12
37
+ AES_256_CBC_CIPHER = 'aes-256-cbc'
37
38
 
38
39
  class << self
39
40
  def error(msg)
@@ -222,7 +223,7 @@ module GoodData
222
223
  # encrypts data with the given key. returns a binary data with the
223
224
  # unhashed random iv in the first 16 bytes
224
225
  def encrypt(data, key)
225
- cipher = OpenSSL::Cipher::Cipher.new('aes-256-cbc')
226
+ cipher = OpenSSL::Cipher::Cipher.new(AES_256_CBC_CIPHER)
226
227
  cipher.encrypt
227
228
  cipher.key = key = Digest::SHA256.digest(key)
228
229
  random_iv = cipher.random_iv
@@ -236,7 +237,7 @@ module GoodData
236
237
 
237
238
  # Simple encrypt data with given key
238
239
  def simple_encrypt(data, key)
239
- cipher = OpenSSL::Cipher::Cipher.new('aes-256-cbc')
240
+ cipher = OpenSSL::Cipher::Cipher.new(AES_256_CBC_CIPHER)
240
241
  cipher.encrypt
241
242
  cipher.key = key
242
243
  encrypted = cipher.update(data)
@@ -253,7 +254,7 @@ module GoodData
253
254
 
254
255
  data = Base64.decode64(database64)
255
256
 
256
- cipher = OpenSSL::Cipher::Cipher.new('aes-256-cbc')
257
+ cipher = OpenSSL::Cipher::Cipher.new(AES_256_CBC_CIPHER)
257
258
  cipher.decrypt
258
259
  cipher.key = cipher_key = Digest::SHA256.digest(key)
259
260
  random_iv = data[0..15] # extract iv from first 16 bytes
@@ -273,7 +274,7 @@ module GoodData
273
274
 
274
275
  data = Base64.decode64(database64)
275
276
 
276
- cipher = OpenSSL::Cipher::Cipher.new('aes-256-cbc')
277
+ cipher = OpenSSL::Cipher::Cipher.new(AES_256_CBC_CIPHER)
277
278
  cipher.decrypt
278
279
  cipher.key = key
279
280
  decrypted = cipher.update(data)
@@ -242,7 +242,7 @@ module GoodData
242
242
 
243
243
  def resolve_reference_params(data_params, params)
244
244
  reference_values = []
245
- regexps = Regexp.union(/\\\\/, /\\\$/, /\$\{(\w+)\}/)
245
+ regexps = Regexp.union(/\\\\/, /\\\$/, /\$\{([^\n\{\}]+)\}/)
246
246
  resolve_reference = lambda do |v|
247
247
  if v.is_a? Hash
248
248
  Hash[
@@ -262,7 +262,7 @@ module GoodData
262
262
  data_params.is_a?(Hash) ? '\\' : '\\\\' # rubocop: disable Metrics/BlockNesting
263
263
  elsif match =~ /\\\$/
264
264
  '$'
265
- elsif match =~ /\$\{(\w+)\}/
265
+ elsif match =~ /\$\{([^\n\{\}]+)\}/
266
266
  val = params["#{$1}"]
267
267
  if val
268
268
  reference_values << val
@@ -43,7 +43,7 @@ module GoodData
43
43
  RESULT_HEADER = [
44
44
  :id,
45
45
  :status,
46
- :originalProject,
46
+ :project,
47
47
  :client,
48
48
  :type
49
49
  ]
@@ -102,7 +102,13 @@ module GoodData
102
102
  options = { delete_projects: delete_projects }
103
103
  options.merge!(delete_extra_option(params, delete_extra)) if delete_extra
104
104
 
105
- domain.update_clients(params.clients, options)
105
+ results = domain.update_clients(params.clients, options)
106
+ # Update status to CREATED if the client has no project
107
+ results&.each do |r|
108
+ r[:status] = 'CREATED' if r[:originalProject].nil?
109
+ r[:project] = r[:originalProject]
110
+ end
111
+ results
106
112
  end
107
113
 
108
114
  private
@@ -8,8 +8,6 @@ require 'gooddata/extensions/integer'
8
8
  require 'gooddata/extensions/string'
9
9
  require 'gooddata/extensions/nil'
10
10
 
11
- require 'active_support/core_ext/hash/compact'
12
-
13
11
  require_relative '../dsl/dsl'
14
12
  require_relative '../helpers/helpers'
15
13
  require_relative '../types/types'
@@ -50,7 +50,8 @@ module GoodData
50
50
  if transfer_all
51
51
  vizs = MdObject.query('visualizationObject', MdObject, client: development_client, project: from_project)
52
52
  viz_widgets = MdObject.query('visualizationWidget', MdObject, client: development_client, project: from_project)
53
- objects = (from_project.reports.to_a + from_project.metrics.to_a + from_project.variables.to_a + vizs.to_a + viz_widgets.to_a).map(&:uri)
53
+ theme = MdObject.query('theme', MdObject, client: development_client, project: from_project)
54
+ objects = (from_project.reports.to_a + from_project.metrics.to_a + from_project.variables.to_a + vizs.to_a + viz_widgets.to_a + theme.to_a).map(&:uri)
54
55
  elsif production_tags.any?
55
56
  objects = from_project.find_by_tag(production_tags)
56
57
  end
@@ -0,0 +1,116 @@
1
+ # frozen_string_literal: true
2
+ # (C) 2019-2020 GoodData Corporation
3
+ require_relative 'base_action'
4
+
5
+ # Migrate date dimension urn:gooddata:date or urn:custom:date to urn:custom_v2:date
6
+ module GoodData
7
+ module LCM2
8
+ class MigrateGdcDateDimension < BaseAction
9
+ DESCRIPTION = 'Migrate Gdc Date Dimension'
10
+ DATE_DIMENSION_CUSTOM_V2 = 'urn:custom_v2:date'
11
+ DATE_DIMENSION_OLD = %w[urn:gooddata:date urn:custom:date]
12
+
13
+ PARAMS = define_params(self) do
14
+ description 'Client Used for Connecting to GD'
15
+ param :gdc_gd_client, instance_of(Type::GdClientType), required: true
16
+
17
+ description 'Specifies how to synchronize LDM and resolve possible conflicts'
18
+ param :synchronize_ldm, instance_of(Type::SynchronizeLDM), required: false, default: 'diff_against_master_with_fallback'
19
+
20
+ description 'Synchronization Info'
21
+ param :synchronize, array_of(instance_of(Type::SynchronizationInfoType)), required: true, generated: true
22
+ end
23
+
24
+ RESULT_HEADER = %i[from to status]
25
+
26
+ class << self
27
+ def call(params)
28
+ results = []
29
+ params.synchronize.map do |segment_info|
30
+ result = migrate_date_dimension(params, segment_info)
31
+ results.concat(result)
32
+ end
33
+
34
+ {
35
+ results: results
36
+ }
37
+ end
38
+
39
+ def migrate_date_dimension(params, segment_info)
40
+ results = []
41
+ client = params.gdc_gd_client
42
+ latest_blueprint = segment_info[:from_blueprint]
43
+ # don't migrate when latest master doesn't contain custom v2 date.
44
+ return results unless contain_v2?(latest_blueprint)
45
+
46
+ previous_blueprint = segment_info[:previous_master]&.blueprint
47
+ # check latest master and previous master
48
+ master_upgrade_datasets = get_upgrade_dates(latest_blueprint, previous_blueprint) if params[:synchronize_ldm].downcase == 'diff_against_master' && previous_blueprint
49
+ unless master_upgrade_datasets&.empty?
50
+ segment_info[:to].pmap do |entry|
51
+ pid = entry[:pid]
52
+ to_project = client.projects(pid) || fail("Invalid 'to' project specified - '#{pid}'")
53
+ to_blueprint = to_project.blueprint
54
+ upgrade_datasets = get_upgrade_dates(latest_blueprint, to_blueprint)
55
+ next if upgrade_datasets.empty?
56
+
57
+ message = get_upgrade_message(upgrade_datasets)
58
+
59
+ results << {
60
+ from: segment_info[:from],
61
+ to: pid,
62
+ status: to_project.upgrade_custom_v2(message)
63
+ }
64
+ end
65
+ end
66
+
67
+ results
68
+ end
69
+
70
+ def get_upgrade_dates(src_blueprint, dest_blueprint)
71
+ dest_dates = get_date_dimensions(dest_blueprint) if dest_blueprint
72
+ src_dates = get_date_dimensions(src_blueprint) if src_blueprint
73
+
74
+ return false if dest_dates.empty? || src_dates.empty?
75
+
76
+ upgrade_datasets = []
77
+ dest_dates.each do |dest|
78
+ src_dim = get_date_dimension(src_blueprint, dest[:id])
79
+ next unless src_dim
80
+
81
+ upgrade_datasets << src_dim[:identifier] if upgrade?(src_dim, dest) && src_dim[:identifier]
82
+ end
83
+
84
+ upgrade_datasets
85
+ end
86
+
87
+ def get_upgrade_message(upgrade_datasets)
88
+ {
89
+ upgrade: {
90
+ dateDatasets: {
91
+ upgrade: "exact",
92
+ datasets: upgrade_datasets
93
+ }
94
+ }
95
+ }
96
+ end
97
+
98
+ def upgrade?(src_dim, dest_dim)
99
+ src_dim[:urn] == DATE_DIMENSION_CUSTOM_V2 && DATE_DIMENSION_OLD.any? { |e| dest_dim[:urn] == e }
100
+ end
101
+
102
+ def contain_v2?(blueprint)
103
+ get_date_dimensions(blueprint).any? { |e| e[:urn] == DATE_DIMENSION_CUSTOM_V2 }
104
+ end
105
+
106
+ def get_date_dimension(blueprint, id)
107
+ GoodData::Model::ProjectBlueprint.find_date_dimension(blueprint, id)
108
+ end
109
+
110
+ def get_date_dimensions(blueprint)
111
+ GoodData::Model::ProjectBlueprint.date_dimensions(blueprint)
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end
@@ -48,7 +48,8 @@ module GoodData
48
48
  domain_name = params.organization || params.domain
49
49
  fail "Either organisation or domain has to be specified in params" unless domain_name
50
50
  domain = client.domain(domain_name) || fail("Invalid domain name specified - #{domain_name}")
51
-
51
+ error_message = nil
52
+ invalid_client_ids = []
52
53
  begin
53
54
  results = params.segments.map do |segment|
54
55
  segment_object = domain.segments(segment.segment_id, data_product)
@@ -57,33 +58,53 @@ module GoodData
57
58
  end
58
59
 
59
60
  unless tmp.empty?
60
- synchronize_projects << {
61
+ synchronize_project = {
61
62
  segment_id: segment.segment_id,
62
63
  from: segment.development_pid,
63
64
  to: tmp.map do |entry|
64
65
  unless entry[:project_uri]
65
- raise "Provisioning project for client id #{entry[:id]} has error: #{entry[:error]}"
66
+ error_message = "There was error during provisioning clients: #{entry[:error]}" unless error_message
67
+ invalid_client_ids << entry[:id]
68
+ next
66
69
  end
67
70
  {
68
71
  pid: entry[:project_uri].split('/').last,
69
72
  client_id: entry[:id]
70
73
  }
71
- end
74
+ end.compact
72
75
  }
76
+
77
+ synchronize_projects << synchronize_project unless synchronize_project[:to].empty?
78
+ end
79
+
80
+ if error_message
81
+ params.gdc_logger.debug "#{error_message}. Purge all invalid clients now ..."
82
+ deleted_client_ids = []
83
+
84
+ segment_object.clients.map do |segment_client|
85
+ project = segment_client.project
86
+ if (project.nil? || project.deleted?)
87
+ client_id = segment_client.client_id
88
+ if invalid_client_ids.include?(client_id)
89
+ segment_client.delete
90
+ deleted_client_ids << client_id
91
+ end
92
+ end
93
+ end
94
+
95
+ params.gdc_logger.debug "Deleted clients: #{deleted_client_ids.join(', ')}"
96
+ raise error_message unless error_message['TooManyProjectsCreatedException'] || error_message['Max number registered projects']
97
+ break tmp
73
98
  end
74
99
 
75
100
  tmp
76
101
  end
77
102
  rescue => e
78
- params.gdc_logger.error "Problem occurs when provisioning clients. Purge all invalid clients now ..."
79
- res = LCM2.run_action PurgeClients, params
80
- params.gdc_logger.debug "Purge clients result: #{res}"
81
- deleted_client_ids = res[:results].select { |r| r[:status] == 'purged' }.map { |r| r[:client_id] }
82
- params.gdc_logger.error "Deleted clients: #{deleted_client_ids.join(', ')}"
103
+ params.gdc_logger.error "Problem occurs when provisioning clients."
83
104
  raise e
84
105
  end
85
106
 
86
- results.flatten!
107
+ results.flatten! if results
87
108
 
88
109
  # Return results
89
110
  {
@@ -49,6 +49,8 @@ module GoodData
49
49
  param :include_deprecated, instance_of(Type::BooleanType), required: false, default: false
50
50
  end
51
51
 
52
+ RESULT_HEADER = %i[from to status]
53
+
52
54
  class << self
53
55
  def call(params)
54
56
  results = []
@@ -76,9 +78,9 @@ module GoodData
76
78
  include_deprecated = params.include_deprecated.to_b
77
79
  from_pid = segment_info[:from]
78
80
  from = params.development_client.projects(from_pid) || fail("Invalid 'from' project specified - '#{from_pid}'")
79
-
80
81
  GoodData.logger.info "Creating Blueprint, project: '#{from.title}', PID: #{from_pid}"
81
82
  blueprint = from.blueprint(include_ca: params.include_computed_attributes.to_b)
83
+ segment_info[:from_blueprint] = blueprint
82
84
  maql_diff = nil
83
85
  previous_master = segment_info[:previous_master]
84
86
  diff_against_master = %w(diff_against_master_with_fallback diff_against_master)
@@ -167,6 +167,10 @@ module GoodData
167
167
  fail "Client id cannot be empty" if client_id.blank?
168
168
 
169
169
  c = all_clients.detect { |specific_client| specific_client.id == client_id }
170
+ if c.nil?
171
+ params.gdc_logger.warn "Client #{client_id} is not found"
172
+ next
173
+ end
170
174
  if params.segments && !segment_uris.include?(c.segment_uri)
171
175
  params.gdc_logger.warn "Client #{client_id} is outside segments_filter #{params.segments}"
172
176
  next
@@ -182,11 +182,11 @@ module GoodData
182
182
  # value of a project id in the data since he does not know it upfront
183
183
  # and we cannot influence its value.
184
184
  common_params = {
185
- domain: domain,
186
- whitelists: whitelists,
187
- ignore_failures: ignore_failures,
188
- remove_users_from_project: remove_users_from_project,
189
- do_not_touch_users_that_are_not_mentioned: do_not_touch_users_that_are_not_mentioned,
185
+ domain: domain,
186
+ whitelists: whitelists,
187
+ ignore_failures: ignore_failures,
188
+ remove_users_from_project: remove_users_from_project,
189
+ do_not_touch_users_that_are_not_mentioned: do_not_touch_users_that_are_not_mentioned,
190
190
  create_non_existing_user_groups: create_non_existing_user_groups,
191
191
  user_groups_cache: nil
192
192
  }
@@ -198,7 +198,8 @@ module GoodData
198
198
  domain.create_users(new_users.uniq { |u| u[:login] || u[:email] })
199
199
  when 'remove_from_organization'
200
200
  user_ids = new_users.uniq { |u| u[:login] || u[:email] }.map { |u| u[:login] || u[:email] }
201
- users = user_ids.map { |u| domain.users(u, client: client) }
201
+ users = user_ids.map { |u| domain.users(u, client: client) }.reject(&:nil?)
202
+ params.gdc_logger.info "#{user_ids.count - users.count} users were not found (or were deleted) in domain #{domain_name}" if user_ids.count > users.count
202
203
  params.gdc_logger.warn "Deleting #{users.count} users from domain #{domain_name}"
203
204
 
204
205
  GoodData.gd_logger.info("Synchronizing in mode=#{mode}, domain=#{domain_name}, data_rows=#{users.count}")
@@ -13,8 +13,6 @@ require 'gooddata/extensions/integer'
13
13
  require 'gooddata/extensions/string'
14
14
  require 'gooddata/extensions/nil'
15
15
 
16
- require 'active_support/core_ext/hash/compact'
17
-
18
16
  require_relative 'actions/actions'
19
17
  require_relative 'dsl/dsl'
20
18
  require_relative 'helpers/helpers'
@@ -138,6 +136,7 @@ module GoodData
138
136
  EnsureTechnicalUsersDomain,
139
137
  EnsureTechnicalUsersProject,
140
138
  SynchronizeLdm,
139
+ MigrateGdcDateDimension,
141
140
  SynchronizeClients,
142
141
  SynchronizeComputedAttributes,
143
142
  CollectDymanicScheduleParams,
@@ -8,8 +8,6 @@
8
8
  require_relative '../dsl/dsl'
9
9
  require_relative '../helpers/helpers'
10
10
 
11
- require 'active_support/core_ext/hash/compact'
12
-
13
11
  require 'gooddata/extensions/class'
14
12
 
15
13
  module GoodData
@@ -1,12 +1,14 @@
1
1
  # encoding: UTF-8
2
2
  #
3
- # Copyright (c) 2010-2017 GoodData Corporation. All rights reserved.
3
+ # Copyright (c) 2010-2021 GoodData Corporation. All rights reserved.
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
7
  module GoodData
8
8
  module Mixin
9
9
  module MdObjectQuery
10
+ ERROR_MESSAGE_NO_PROJECT = 'No :project specified'
11
+
10
12
  # Method intended to get all objects of that type in a specified project
11
13
  #
12
14
  # @param options [Hash] the options hash
@@ -38,7 +40,7 @@ module GoodData
38
40
  fail ArgumentError, 'No :client specified' if client.nil?
39
41
 
40
42
  p = options[:project]
41
- fail ArgumentError, 'No :project specified' if p.nil?
43
+ fail ArgumentError, ERROR_MESSAGE_NO_PROJECT if p.nil?
42
44
 
43
45
  project = GoodData::Project[p, options]
44
46
  fail ArgumentError, 'Wrong :project specified' if project.nil?
@@ -98,10 +100,10 @@ module GoodData
98
100
  # Returns which objects uses this MD resource
99
101
  def usedby(uri, key = nil, opts = { :client => GoodData.connection, :project => GoodData.project })
100
102
  p = opts[:project]
101
- fail ArgumentError, 'No :project specified' if p.nil?
103
+ fail ArgumentError, ERROR_MESSAGE_NO_PROJECT if p.nil?
102
104
 
103
105
  project = GoodData::Project[p, opts]
104
- fail ArgumentError, 'No :project specified' if project.nil?
106
+ fail ArgumentError, ERROR_MESSAGE_NO_PROJECT if project.nil?
105
107
 
106
108
  dependency("#{project.md['usedby2']}/#{uri_obj_id(uri)}", key, opts)
107
109
  end
@@ -111,10 +113,10 @@ module GoodData
111
113
  # Returns which objects this MD resource uses
112
114
  def using(uri, key = nil, opts = { :client => GoodData.connection, :project => GoodData.project })
113
115
  p = opts[:project]
114
- fail ArgumentError, 'No :project specified' if p.nil?
116
+ fail ArgumentError, ERROR_MESSAGE_NO_PROJECT if p.nil?
115
117
 
116
118
  project = GoodData::Project[p, opts]
117
- fail ArgumentError, 'No :project specified' if project.nil?
119
+ fail ArgumentError, ERROR_MESSAGE_NO_PROJECT if project.nil?
118
120
 
119
121
  dependency("#{project.md['using2']}/#{uri_obj_id(uri)}", key, opts)
120
122
  end