gooddata 2.1.11 → 2.1.16

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of gooddata might be problematic. Click here for more details.

Files changed (57) 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 +4 -3
  5. data/CHANGELOG.md +34 -0
  6. data/Dockerfile +9 -3
  7. data/LICENSE +30 -22
  8. data/LICENSE.rb +1 -1
  9. data/LICENSE_FOR_RUBY_SDK_COMPONENT.txt +4423 -0
  10. data/NOTICES.txt +6692 -0
  11. data/README.md +19 -1
  12. data/Rakefile +5 -1
  13. data/SDK_VERSION +1 -1
  14. data/VERSION +1 -1
  15. data/bin/run_brick.rb +3 -0
  16. data/bin/test_projects_cleanup.rb +7 -3
  17. data/ci/postgresql/pom.xml +57 -0
  18. data/dev-gooddata-sso.pub.encrypted +40 -40
  19. data/gdc_fossa_lcm.yaml +2 -0
  20. data/gdc_fossa_ruby_sdk.yaml +5 -0
  21. data/gooddata.gemspec +3 -4
  22. data/k8s/charts/lcm-bricks/Chart.yaml +1 -1
  23. data/k8s/charts/lcm-bricks/templates/prometheus/alertingRules.yaml +32 -12
  24. data/lib/gooddata.rb +2 -0
  25. data/lib/gooddata/cloud_resources/{cloud_resouce_factory.rb → cloud_resource_factory.rb} +8 -0
  26. data/lib/gooddata/cloud_resources/cloud_resources.rb +1 -1
  27. data/lib/gooddata/cloud_resources/postgresql/drivers/.gitkeepme +0 -0
  28. data/lib/gooddata/cloud_resources/postgresql/postgresql_client.rb +107 -0
  29. data/lib/gooddata/commands/scaffold.rb +9 -10
  30. data/lib/gooddata/core/nil_logger.rb +3 -1
  31. data/lib/gooddata/helpers/data_helper.rb +1 -2
  32. data/lib/gooddata/helpers/global_helpers.rb +6 -5
  33. data/lib/gooddata/helpers/global_helpers_params.rb +2 -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/synchronize_ldm.rb +10 -1
  38. data/lib/gooddata/lcm/actions/synchronize_user_filters.rb +4 -0
  39. data/lib/gooddata/lcm/actions/synchronize_users.rb +7 -6
  40. data/lib/gooddata/lcm/lcm2.rb +1 -2
  41. data/lib/gooddata/lcm/types/base_type.rb +0 -2
  42. data/lib/gooddata/mixins/md_object_query.rb +8 -6
  43. data/lib/gooddata/models/blueprint/project_blueprint.rb +0 -2
  44. data/lib/gooddata/models/client.rb +14 -12
  45. data/lib/gooddata/models/data_source.rb +664 -0
  46. data/lib/gooddata/models/domain.rb +1 -2
  47. data/lib/gooddata/models/from_wire.rb +1 -0
  48. data/lib/gooddata/models/metadata/scheduled_mail.rb +1 -1
  49. data/lib/gooddata/models/process.rb +11 -3
  50. data/lib/gooddata/models/project.rb +128 -19
  51. data/lib/gooddata/models/user_filters/user_filter_builder.rb +0 -1
  52. data/lib/gooddata/models/user_group.rb +0 -1
  53. data/lib/gooddata/rest/connection.rb +6 -4
  54. data/rubydev_public.gpg.encrypted +51 -51
  55. data/rubydev_secret_keys.gpg.encrypted +109 -109
  56. metadata +19 -25
  57. data/DEPENDENCIES.md +0 -880
@@ -4,7 +4,7 @@
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 'erubis'
7
+ require 'erb'
8
8
  require 'fileutils'
9
9
  require 'pathname'
10
10
 
@@ -18,15 +18,14 @@ module GoodData
18
18
  # TODO: Add option for custom output dir
19
19
  def project(name)
20
20
  fail ArgumentError, 'No name specified' if name.nil?
21
-
22
21
  FileUtils.mkdir(name)
23
22
  FileUtils.cd(name) do
24
23
  FileUtils.mkdir('model')
25
24
  FileUtils.cd('model') do
26
25
  input = File.read(TEMPLATES_PATH + 'project/model/model.rb.erb')
27
- eruby = Erubis::Eruby.new(input)
26
+ erb = ERB.new(input)
28
27
  File.open('model.rb', 'w') do |f|
29
- f.write(eruby.result(:name => name))
28
+ f.write(erb.result_with_hash(:name => name))
30
29
  end
31
30
  end
32
31
 
@@ -36,9 +35,9 @@ module GoodData
36
35
  end
37
36
 
38
37
  input = File.read(TEMPLATES_PATH + 'project/Goodfile.erb')
39
- eruby = Erubis::Eruby.new(input)
38
+ erb = ERB.new(input)
40
39
  File.open('Goodfile', 'w') do |f|
41
- f.write(eruby.result)
40
+ f.write(erb.result)
42
41
  end
43
42
  end
44
43
  end
@@ -51,15 +50,15 @@ module GoodData
51
50
  FileUtils.mkdir(name)
52
51
  FileUtils.cd(name) do
53
52
  input = File.read(TEMPLATES_PATH + 'bricks/brick.rb.erb')
54
- eruby = Erubis::Eruby.new(input)
53
+ erb = ERB.new(input)
55
54
  File.open('brick.rb', 'w') do |f|
56
- f.write(eruby.result)
55
+ f.write(erb.result)
57
56
  end
58
57
 
59
58
  input = File.read(TEMPLATES_PATH + 'bricks/main.rb.erb')
60
- eruby = Erubis::Eruby.new(input)
59
+ erb = ERB.new(input)
61
60
  File.open('main.rb', 'w') do |f|
62
- f.write(eruby.result)
61
+ f.write(erb.result)
63
62
  end
64
63
  end
65
64
  end
@@ -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
@@ -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
@@ -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)
@@ -89,6 +91,13 @@ module GoodData
89
91
  maql_diff_params << :excludeFactRule if exclude_fact_rule
90
92
  maql_diff_params << :includeDeprecated if include_deprecated
91
93
  maql_diff = previous_master.maql_diff(blueprint: blueprint, params: maql_diff_params)
94
+ chunks = maql_diff['projectModelDiff']['updateScripts']
95
+ if chunks.empty?
96
+ GoodData.logger.info "Synchronize LDM to clients will not proceed in mode \
97
+ '#{params[:synchronize_ldm].downcase}' due to no LDM changes in the new master project. \
98
+ If you had changed LDM of clients manually, please use mode 'diff_against_clients' \
99
+ to force synchronize LDM to clients"
100
+ end
92
101
  end
93
102
 
94
103
  segment_info[:to] = segment_info[:to].pmap do |entry|
@@ -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
@@ -2,8 +2,6 @@
2
2
  # This source code is licensed under the BSD-style license found in the
3
3
  # LICENSE file in the root directory of this source tree.
4
4
 
5
- require 'active_support/core_ext/hash/compact'
6
-
7
5
  module GoodData
8
6
  module Model
9
7
  class ProjectBlueprint