gooddata 0.6.10 → 0.6.11

Sign up to get free protection for your applications and to get access to all the features.
Files changed (92) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +9 -0
  3. data/.travis.yml +4 -0
  4. data/CHANGELOG.md +6 -0
  5. data/README.md +1 -0
  6. data/gooddata.gemspec +38 -40
  7. data/lib/gooddata/bricks/base_downloader.rb +1 -1
  8. data/lib/gooddata/bricks/middleware/base_middleware.rb +36 -0
  9. data/lib/gooddata/bricks/middleware/bulk_salesforce_middleware.rb +8 -38
  10. data/lib/gooddata/bricks/middleware/decode_params_middleware.rb +14 -0
  11. data/lib/gooddata/bricks/middleware/fs_upload_middleware.rb +7 -6
  12. data/lib/gooddata/bricks/middleware/gooddata_middleware.rb +7 -5
  13. data/lib/gooddata/bricks/middleware/logger_middleware.rb +1 -1
  14. data/lib/gooddata/bricks/middleware/restforce_middleware.rb +20 -14
  15. data/lib/gooddata/bricks/middleware/undot_params_middleware.rb +33 -0
  16. data/lib/gooddata/cli/commands/api_cmd.rb +1 -1
  17. data/lib/gooddata/cli/commands/auth_cmd.rb +1 -1
  18. data/lib/gooddata/cli/commands/console_cmd.rb +2 -2
  19. data/lib/gooddata/cli/commands/process_cmd.rb +54 -7
  20. data/lib/gooddata/cli/commands/project_cmd.rb +9 -9
  21. data/lib/gooddata/cli/commands/projects_cmd.rb +1 -1
  22. data/lib/gooddata/cli/commands/run_ruby_cmd.rb +24 -7
  23. data/lib/gooddata/cli/commands/scaffold_cmd.rb +2 -2
  24. data/lib/gooddata/cli/commands/user_cmd.rb +1 -1
  25. data/lib/gooddata/cli/hooks.rb +3 -3
  26. data/lib/gooddata/commands/datasets.rb +1 -1
  27. data/lib/gooddata/commands/project.rb +2 -2
  28. data/lib/gooddata/commands/role.rb +1 -1
  29. data/lib/gooddata/commands/runners.rb +2 -2
  30. data/lib/gooddata/connection.rb +2 -2
  31. data/lib/gooddata/core/nil_logger.rb +1 -1
  32. data/lib/gooddata/core/rest.rb +12 -8
  33. data/lib/gooddata/data/guesser.rb +1 -1
  34. data/lib/gooddata/exceptions/attr_element_not_found.rb +1 -1
  35. data/lib/gooddata/extensions/enumerable.rb +1 -1
  36. data/lib/gooddata/extensions/hash.rb +20 -0
  37. data/lib/gooddata/helpers/csv_helper.rb +1 -1
  38. data/lib/gooddata/helpers/global_helpers.rb +59 -1
  39. data/lib/gooddata/mixins/md_lock.rb +83 -0
  40. data/lib/gooddata/mixins/md_object_indexer.rb +1 -1
  41. data/lib/gooddata/mixins/md_object_query.rb +1 -1
  42. data/lib/gooddata/mixins/md_relations.rb +0 -9
  43. data/lib/gooddata/models/dashboard_builder.rb +1 -1
  44. data/lib/gooddata/models/domain.rb +2 -2
  45. data/lib/gooddata/models/empty_result.rb +5 -5
  46. data/lib/gooddata/models/execution.rb +74 -0
  47. data/lib/gooddata/models/execution_detail.rb +74 -0
  48. data/lib/gooddata/models/membership.rb +1 -1
  49. data/lib/gooddata/models/metadata/attribute.rb +4 -6
  50. data/lib/gooddata/models/metadata/dashboard.rb +2 -0
  51. data/lib/gooddata/models/metadata/fact.rb +2 -2
  52. data/lib/gooddata/models/metadata/metric.rb +4 -1
  53. data/lib/gooddata/models/metadata/report.rb +84 -34
  54. data/lib/gooddata/models/metadata/report_definition.rb +28 -17
  55. data/lib/gooddata/models/metadata.rb +1 -1
  56. data/lib/gooddata/models/model.rb +1 -1
  57. data/lib/gooddata/models/process.rb +70 -54
  58. data/lib/gooddata/models/profile.rb +47 -10
  59. data/lib/gooddata/models/project.rb +74 -30
  60. data/lib/gooddata/models/project_blueprint.rb +9 -10
  61. data/lib/gooddata/models/project_builder.rb +2 -2
  62. data/lib/gooddata/models/project_creator.rb +4 -4
  63. data/lib/gooddata/models/report_data_result.rb +1 -1
  64. data/lib/gooddata/models/schedule.rb +39 -32
  65. data/lib/gooddata/models/to_manifest.rb +5 -5
  66. data/lib/gooddata/models/to_wire.rb +3 -3
  67. data/lib/gooddata/rest/client.rb +64 -31
  68. data/lib/gooddata/rest/connection.rb +7 -7
  69. data/lib/gooddata/rest/connections/dummy_connection.rb +5 -5
  70. data/lib/gooddata/rest/connections/rest_client_connection.rb +106 -44
  71. data/lib/gooddata/rest/object.rb +1 -1
  72. data/lib/gooddata/version.rb +1 -1
  73. data/spec/bricks/bricks_spec.rb +67 -0
  74. data/spec/bricks/default-config.json +8 -0
  75. data/spec/data/gooddata_version_process/gooddata_version.rb +3 -0
  76. data/spec/data/gooddata_version_process/gooddata_version.zip +0 -0
  77. data/spec/data/ruby_params_process/ruby_params.rb +3 -0
  78. data/spec/helpers/process_helper.rb +12 -0
  79. data/spec/helpers/schedule_helper.rb +21 -0
  80. data/spec/integration/create_project_spec.rb +19 -0
  81. data/spec/integration/full_process_schedule_spec.rb +129 -8
  82. data/spec/integration/full_project_spec.rb +52 -2
  83. data/spec/spec_helper.rb +1 -0
  84. data/spec/unit/core/rest_spec.rb +76 -3
  85. data/spec/unit/helpers_spec.rb +43 -0
  86. data/spec/unit/models/metric_spec.rb +33 -0
  87. data/spec/unit/models/params_spec.rb +96 -0
  88. data/spec/unit/models/profile_spec.rb +16 -0
  89. data/spec/unit/models/schedule_spec.rb +12 -3
  90. metadata +350 -187
  91. data/lib/gooddata/helper/class_helper.rb +0 -1
  92. data/lib/gooddata/helper/helpers.rb +0 -8
@@ -13,7 +13,7 @@ GoodData::CLI.module_eval do
13
13
  command :project do |c|
14
14
  c.desc 'If you are in a gooddata project blueprint or if you provide a project id it will start an interactive session inside that project'
15
15
  c.command :jack_in do |jack|
16
- jack.action do |global_options, options, args|
16
+ jack.action do |global_options, options, _args|
17
17
  opts = options.merge(global_options)
18
18
  GoodData::Command::Project.jack_in(opts)
19
19
  end
@@ -44,7 +44,7 @@ GoodData::CLI.module_eval do
44
44
 
45
45
  c.desc 'Delete a project. Be careful this is impossible to revert'
46
46
  c.command :delete do |delete|
47
- delete.action do |global_options, options, args|
47
+ delete.action do |global_options, options, _args|
48
48
  id = global_options[:project_id]
49
49
  opts = options.merge(global_options)
50
50
  client = GoodData.connect(opts)
@@ -66,7 +66,7 @@ GoodData::CLI.module_eval do
66
66
  clone.default_value true
67
67
  clone.switch [:d, :data]
68
68
 
69
- clone.action do |global_options, options, args|
69
+ clone.action do |global_options, options, _args|
70
70
  opts = options.merge(global_options)
71
71
  id = global_options[:project_id]
72
72
  token = opts[:token]
@@ -103,7 +103,7 @@ GoodData::CLI.module_eval do
103
103
 
104
104
  c.desc 'List users'
105
105
  c.command :users do |list|
106
- list.action do |global_options, options, args|
106
+ list.action do |global_options, options, _args|
107
107
  opts = options.merge(global_options)
108
108
  client = GoodData.connect(opts)
109
109
 
@@ -117,7 +117,7 @@ GoodData::CLI.module_eval do
117
117
 
118
118
  c.desc 'Shows basic info about a project'
119
119
  c.command :show do |show|
120
- show.action do |global_options, options, args|
120
+ show.action do |global_options, options, _args|
121
121
  id = global_options[:project_id]
122
122
  opts = options.merge(global_options)
123
123
  client = GoodData.connect(opts)
@@ -128,7 +128,7 @@ GoodData::CLI.module_eval do
128
128
 
129
129
  c.desc 'If you are in a gooddata project blueprint it will apply the changes. If you do not provide a project id it will build it from scratch and create a project for you.'
130
130
  c.command :build do |show|
131
- show.action do |global_options, options, args|
131
+ show.action do |global_options, options, _args|
132
132
  opts = options.merge(global_options)
133
133
  client = GoodData.connect(opts)
134
134
  spec, _ = GoodData::Command::Project.get_spec_and_project_id('.')
@@ -139,7 +139,7 @@ GoodData::CLI.module_eval do
139
139
 
140
140
  c.desc 'If you are in a gooddata project blueprint it will apply the changes. If you do not provide a project id it will build it from scratch and create a project for you.'
141
141
  c.command :update do |show|
142
- show.action do |global_options, options, args|
142
+ show.action do |global_options, options, _args|
143
143
 
144
144
  opts = options.merge(global_options)
145
145
  GoodData.connect(opts)
@@ -153,7 +153,7 @@ GoodData::CLI.module_eval do
153
153
 
154
154
  c.desc 'Shows roles in the project'
155
155
  c.command :roles do |roles|
156
- roles.action do |global_options, options, args|
156
+ roles.action do |global_options, options, _args|
157
157
  project_id = global_options[:project_id]
158
158
  fail 'Project ID has to be provided' if project_id.nil? || project_id.empty?
159
159
 
@@ -167,7 +167,7 @@ GoodData::CLI.module_eval do
167
167
 
168
168
  c.desc 'You can run project validation which will check RI and other problems.'
169
169
  c.command :validate do |show|
170
- show.action do |global_options, options, args|
170
+ show.action do |global_options, options, _args|
171
171
  opts = options.merge(global_options)
172
172
  client = GoodData.connect(opts)
173
173
  pp GoodData::Command::Project.validate(global_options[:project_id], opts.merge(client: client))
@@ -10,7 +10,7 @@ GoodData::CLI.module_eval do
10
10
 
11
11
  c.desc "Lists user's projects"
12
12
  c.command :list do |list|
13
- list.action do |global_options, options, args|
13
+ list.action do |global_options, options, _args|
14
14
  opts = options.merge(global_options)
15
15
  client = GoodData.connect(opts)
16
16
  list = GoodData::Command::Projects.list(client: client)
@@ -1,16 +1,23 @@
1
1
  # encoding: UTF-8
2
2
 
3
3
  require 'pp'
4
+ require 'hashie'
4
5
 
5
6
  require_relative '../shared'
6
7
  require_relative '../../commands/process'
7
8
  require_relative '../../commands/runners'
8
9
  require_relative '../../client'
9
10
 
11
+ # translate given params (with dots) to json-like params
12
+ def load_undot(filename)
13
+ p = MultiJson.load(File.read(filename)).extend(Hashie::Extensions::DeepMerge)
14
+ p.undot
15
+ end
16
+
10
17
  GoodData::CLI.module_eval do
11
18
 
12
19
  desc 'Run ruby bricks either locally or remotely deployed on our server. Currently private alpha.'
13
- # arg_name 'show'
20
+ # arg_name 'show'
14
21
  command :run_ruby do |c|
15
22
 
16
23
  c.desc 'Directory of the ruby brick'
@@ -21,10 +28,14 @@ GoodData::CLI.module_eval do
21
28
  c.default_value nil
22
29
  c.flag [:l, :logger]
23
30
 
24
- c.desc 'Params file path. Inside should be hash of key values'
31
+ c.desc 'Params file path. Inside should be hash of key values. These params override any defaults given in bricks.'
25
32
  c.default_value nil
26
33
  c.flag [:params]
27
34
 
35
+ c.desc 'Remote system credentials file path. Inside should be hash of key values.'
36
+ c.default_value nil
37
+ c.flag [:credentials]
38
+
28
39
  c.desc 'Run on remote machine'
29
40
  c.switch [:r, :remote]
30
41
 
@@ -32,14 +43,21 @@ GoodData::CLI.module_eval do
32
43
  c.default_value nil
33
44
  c.flag [:n, :name]
34
45
 
35
- c.action do |global_options, options, args|
46
+ c.action do |global_options, options, _args|
36
47
  verbose = global_options[:verbose]
37
48
  options[:expanded_params] = if options[:params]
38
- MultiJson.load(File.read(options[:params]))
49
+ # load params and credentials if given
50
+ runtime_params = load_undot(options[:params])
51
+ if options[:credentials]
52
+ runtime_params = runtime_params.deep_merge(load_undot(options[:credentials]))
53
+ end
54
+ { 'config' => runtime_params }
39
55
  else
40
- {}
56
+ { 'config' => {} }
41
57
  end
42
-
58
+ # if there are some GDC_* params in config, put them on the level above
59
+ gdc_params = options[:expanded_params]['config'].select { |k, _| k =~ /GDC_.*/ }
60
+ options[:expanded_params].merge!(gdc_params)
43
61
  opts = options.merge(global_options).merge(:type => 'RUBY')
44
62
  GoodData.connect(opts)
45
63
  if options[:remote]
@@ -53,5 +71,4 @@ GoodData::CLI.module_eval do
53
71
  puts HighLine.color('Running ruby brick - DONE', HighLine::GREEN) if verbose
54
72
  end
55
73
  end
56
-
57
74
  end
@@ -13,7 +13,7 @@ GoodData::CLI.module_eval do
13
13
 
14
14
  c.desc 'Scaffold a gooddata project blueprint'
15
15
  c.command :project do |project|
16
- project.action do |global_options, options, args|
16
+ project.action do |_global_options, _options, args|
17
17
  name = args.first
18
18
  fail 'Name of the project has to be provided' if name.nil? || name.empty?
19
19
  GoodData::Command::Scaffold.project(name)
@@ -23,7 +23,7 @@ GoodData::CLI.module_eval do
23
23
  c.desc 'Scaffold a gooddata ruby brick. This is a piece of code that you can run on our platform'
24
24
  c.command :brick do |brick|
25
25
  # brick.arg_name 'name'
26
- brick.action do |global_options, options, args|
26
+ brick.action do |_global_options, _options, args|
27
27
  name = args.first
28
28
  fail 'Name of the brick has to be provided' if name.nil? || name.empty?
29
29
  GoodData::Command::Scaffold.brick(name)
@@ -11,7 +11,7 @@ GoodData::CLI.module_eval do
11
11
  command :user do |c|
12
12
  c.desc 'Show your profile'
13
13
  c.command :show do |show|
14
- show.action do |global_options, options, args|
14
+ show.action do |global_options, options, _args|
15
15
  opts = options.merge(global_options)
16
16
  client = GoodData.connect(opts)
17
17
  pp GoodData::Command::User.show(client: client)
@@ -6,7 +6,7 @@ require 'pp'
6
6
  require_relative '../helpers/auth_helpers'
7
7
 
8
8
  GoodData::CLI.module_eval do
9
- pre do |global, command, options, args|
9
+ pre do |global, _command, _options, _args|
10
10
  require 'logger'
11
11
  GoodData.logger = Logger.new(STDOUT) if global[:l]
12
12
  username = global[:username]
@@ -30,13 +30,13 @@ GoodData::CLI.module_eval do
30
30
  true
31
31
  end
32
32
 
33
- post do |global, command, options, args|
33
+ post do |_global, _command, _options, _args|
34
34
  # Post logic here
35
35
  # Use skips_post before a command to skip this
36
36
  # block on that command only
37
37
  end
38
38
 
39
- on_error do |exception|
39
+ on_error do |_exception|
40
40
  # Error logic here
41
41
  # return false to skip default error handling
42
42
  # binding.pry
@@ -125,7 +125,7 @@ module GoodData
125
125
  connection_point_set = false
126
126
  question_fmt = 'Select data type of column #%i (%s)'
127
127
  guesser.headers.each_with_index do |header, i|
128
- options = guess[header].map { |t| t.to_s }
128
+ options = guess[header].map(&:to_s)
129
129
  options = options.select { |t| t != :connection_point.to_s } if connection_point_set
130
130
  type = ask question_fmt % [i + 1, header], :answers => options
131
131
  model.push :title => header, :name => header, :type => type.upcase
@@ -108,7 +108,7 @@ module GoodData
108
108
  def jack_in(options)
109
109
  goodfile_path = GoodData::Helpers.find_goodfile(Pathname('.'))
110
110
 
111
- spin_session = proc do |goodfile, blueprint|
111
+ spin_session = proc do |goodfile, _blueprint|
112
112
  project_id = options[:project_id] || goodfile[:project_id]
113
113
  message = 'You have to provide "project_id". You can either provide it through -p flag'\
114
114
  'or even better way is to fill it in in your Goodfile under key "project_id".'\
@@ -125,7 +125,7 @@ module GoodData
125
125
 
126
126
  puts "Use 'exit' to quit the live session. Use 'q' to jump out of displaying a large output."
127
127
  binding.pry(:quiet => true,
128
- :prompt => [proc do |target_self, nest_level, pry|
128
+ :prompt => [proc do |_target_self, _nest_level, _pry|
129
129
  'project_live_sesion: '
130
130
  end])
131
131
  end
@@ -6,7 +6,7 @@ module GoodData
6
6
  module Command
7
7
  class Role
8
8
  class << self
9
- def list(pid, opts = { :client => GoodData.connection, :project => GoodData.project })
9
+ def list(_pid, opts = { :client => GoodData.connection, :project => GoodData.project })
10
10
  p = opts[:project]
11
11
  fail ArgumentError, 'No :project specified' if p.nil?
12
12
 
@@ -13,8 +13,8 @@ module GoodData
13
13
 
14
14
  params = options[:expanded_params] || {}
15
15
 
16
- GoodData.connection.connect!
17
- sst = GoodData.connection.cookies[:cookies]['GDCAuthSST']
16
+ client = GoodData.connect(options[:username], options[:password])
17
+ sst = client.connection.sst_token
18
18
  pwd = Pathname.new(Dir.pwd)
19
19
 
20
20
  server_uri = URI(options[:server]) unless options[:server].nil?
@@ -35,8 +35,8 @@ module GoodData
35
35
  connection = connect(options, second_options, third_options)
36
36
  bl.call(connection)
37
37
  rescue Exception => e # rubocop:disable RescueException
38
- puts e.message
39
- raise e
38
+ puts e.message
39
+ raise e
40
40
  ensure
41
41
  disconnect
42
42
  end
@@ -3,7 +3,7 @@
3
3
  module GoodData
4
4
  # Dummy implementation of logger
5
5
  class NilLogger
6
- def debug(*args)
6
+ def debug(*_args)
7
7
  end
8
8
 
9
9
  alias_method :info, :debug
@@ -67,25 +67,29 @@ module GoodData
67
67
  def upload_to_user_webdav(file, options = {})
68
68
  u = URI(GoodData.project.links['uploads'])
69
69
  url = URI.join(u.to_s.chomp(u.path.to_s), '/uploads/')
70
+
70
71
  connection.upload(file, options.merge(
71
- :directory => options[:directory],
72
72
  :staging_url => url
73
73
  ))
74
74
  end
75
75
 
76
- def get_project_webdav_path(file, options = {})
76
+ def get_project_webdav_path(_file, _options = {})
77
77
  u = URI(GoodData.project.links['uploads'])
78
78
  URI.join(u.to_s.chomp(u.path.to_s), '/project-uploads/', "#{GoodData.project.pid}/")
79
79
  end
80
80
 
81
81
  def upload_to_project_webdav(file, options = {})
82
+ webdav_filename = File.basename(file)
83
+ url = get_project_webdav_path(webdav_filename, options)
84
+ connection.upload(file, options.merge(:staging_url => url))
85
+ end
86
+
87
+ def download_from_project_webdav(file, where, options = {})
82
88
  url = get_project_webdav_path(file, options)
83
- connection.upload(file, options.merge(
84
- :directory => options[:directory],
85
- :staging_url => url))
89
+ connection.download(file, where, options.merge(:staging_url => url))
86
90
  end
87
91
 
88
- def get_user_webdav_path(file, options = {})
92
+ def get_user_webdav_path(_file, _options = {})
89
93
  u = URI(GoodData.project.links['uploads'])
90
94
  URI.join(u.to_s.chomp(u.path.to_s), '/uploads/')
91
95
  end
@@ -110,7 +114,7 @@ module GoodData
110
114
  response = GoodData.get(link, :process => false)
111
115
  while response.code == code
112
116
  sleep sleep_interval
113
- GoodData.connection.retryable(:tries => 3, :on => RestClient::InternalServerError) do
117
+ GoodData::Rest::Client.retryable(:tries => 3, :on => RestClient::InternalServerError) do
114
118
  sleep sleep_interval
115
119
  response = GoodData.get(link, :process => false)
116
120
  end
@@ -139,7 +143,7 @@ module GoodData
139
143
  response = get(link)
140
144
  while bl.call(response)
141
145
  sleep sleep_interval
142
- client.retryable(:tries => 3, :on => RestClient::InternalServerError) do
146
+ GoodData::Rest::Client.retryable(:tries => 3, :on => RestClient::InternalServerError) do
143
147
  sleep sleep_interval
144
148
  response = get(link)
145
149
  end
@@ -24,7 +24,7 @@ module GoodData
24
24
 
25
25
  def initialize(reader)
26
26
  @reader = reader
27
- @headers = reader.shift.map! { |h| h.to_s } || fail('Empty data set')
27
+ @headers = reader.shift.map!(&:to_s) || fail('Empty data set')
28
28
  @pros = {}
29
29
  @cons = {}
30
30
  @seen = {}
@@ -5,7 +5,7 @@ module GoodData
5
5
  class AttributeElementNotFound < RuntimeError
6
6
  DEFAULT_MSG = 'Attribute element "%s" was not found'
7
7
 
8
- def initialize(value, msg = DEFAULT_MSG)
8
+ def initialize(value, _msg = DEFAULT_MSG)
9
9
  super(sprintf(DEFAULT_MSG, value))
10
10
  end
11
11
  end
@@ -22,6 +22,6 @@ module Enumerable
22
22
 
23
23
  def pselect(&block)
24
24
  intermediate = pmap(&block)
25
- zip(intermediate).select { |x| x[1] }.map { |x| x.first }
25
+ zip(intermediate).select { |x| x[1] }.map(&:first)
26
26
  end
27
27
  end
@@ -1,6 +1,11 @@
1
1
  # encoding: UTF-8
2
+ require 'hashie'
3
+
4
+ require 'hashie/extensions/deep_merge'
2
5
 
3
6
  class Hash
7
+ include Hashie::Extensions::DeepMerge
8
+
4
9
  # Return a hash that includes everything but the given keys. This is useful for
5
10
  # limiting a set of parameters to everything but a few known toggles:
6
11
  #
@@ -22,4 +27,19 @@ class Hash
22
27
  keys.each { |key| delete(key) }
23
28
  self
24
29
  end
30
+
31
+ def undot
32
+ # for each key-value config given
33
+ hashes = map do |k, v|
34
+ # dot notation to hash
35
+ k.split('__').reverse.reduce(v) do |memo, obj|
36
+ { obj => memo }.extend(Hashie::Extensions::DeepMerge)
37
+ end
38
+ end
39
+
40
+ # merge back the keys as they came
41
+ hashes.reduce do |memo, obj|
42
+ memo.deep_merge(obj)
43
+ end
44
+ end
25
45
  end
@@ -38,7 +38,7 @@ module GoodData
38
38
  # @option opts [String] :path File to write data to
39
39
  # @option opts [Array] :data Mandatory array of data to write
40
40
  # @option opts [String] :header Optional Header row
41
- def write(opts, &block)
41
+ def write(opts, &_block)
42
42
  path = opts[:path]
43
43
  header = opts[:header]
44
44
  data = opts[:data]
@@ -18,6 +18,57 @@ module GoodData
18
18
  RUBY_PLATFORM =~ /-darwin\d/
19
19
  end
20
20
 
21
+ ENCODED_PARAMS_KEY = :gd_encoded_params
22
+ ENCODED_HIDDEN_PARAMS_KEY = :gd_encoded_hidden_params
23
+
24
+ # Encodes parameters for passing them to GD execution platform.
25
+ # Core types are kept and complex types (arrays, structures, etc) are JSON encoded into key hash "gd_encoded_params" or "gd_encoded_hidden_params", depending on the 'hidden' method param.
26
+ # The two different keys are used because the params and hidden params are merged by the platform and if we use the same key, the param would be overwritten.
27
+ #
28
+ # Core types are following:
29
+ # - Boolean (true, false)
30
+ # - Fixnum
31
+ # - Float
32
+ # - Nil
33
+ # - String
34
+ #
35
+ # @param [Hash] params Parameters to be encoded
36
+ # @return [Hash] Encoded parameters
37
+ def encode_params(params, hidden = false)
38
+ res = {}
39
+ nested = {}
40
+ core_types = [FalseClass, Fixnum, Float, NilClass, TrueClass, String]
41
+ params.each do |k, v|
42
+ if core_types.include?(v.class)
43
+ res[k] = v
44
+ else
45
+ nested[k] = v
46
+ end
47
+ end
48
+ key = hidden ? ENCODED_PARAMS_KEY : ENCODED_HIDDEN_PARAMS_KEY
49
+ res[key] = nested.to_json unless nested.empty?
50
+ res
51
+ end
52
+
53
+ # Decodes params as they came from the platform
54
+ # The "data" key is supposed to be json and it's parsed - if this
55
+ def decode_params(params)
56
+ key = ENCODED_PARAMS_KEY.to_s
57
+ hidden_key = ENCODED_HIDDEN_PARAMS_KEY.to_s
58
+ data_params = params[key] || '{}'
59
+ hidden_data_params = params[hidden_key] || '{}'
60
+
61
+ begin
62
+ parsed_data_params = JSON.parse(data_params)
63
+ parsed_hidden_data_params = JSON.parse(hidden_data_params)
64
+ rescue JSON::ParserError => e
65
+ raise e.class, "Error reading json from '#{key}' or '#{hidden_key}' in params #{params}\n #{e.message}"
66
+ end
67
+ params.delete(key)
68
+ params.delete(hidden_key)
69
+ params.merge(parsed_data_params).merge(parsed_hidden_data_params)
70
+ end
71
+
21
72
  def error(msg)
22
73
  STDERR.puts(msg)
23
74
  exit 1
@@ -36,8 +87,15 @@ module GoodData
36
87
  nil
37
88
  end
38
89
 
90
+ def get_path(an_object, path = [])
91
+ return an_object if path.empty?
92
+ path.reduce(an_object) do |a, e|
93
+ a && a.key?(e) ? a[e] : nil
94
+ end
95
+ end
96
+
39
97
  def hash_dfs(thing, &block)
40
- if !thing.is_a?(Hash) && !thing.is_a?(Array)
98
+ if !thing.is_a?(Hash) && !thing.is_a?(Array) # rubocop:disable Style/GuardClause
41
99
  elsif thing.is_a?(Array)
42
100
  thing.each do |child|
43
101
  hash_dfs(child, &block)
@@ -0,0 +1,83 @@
1
+ # encoding: UTF-8
2
+
3
+ module GoodData
4
+ module Mixin
5
+ module Lockable
6
+ # Locks an object. Locked object cannot be changed by certain users.
7
+ #
8
+ # @return [GoodData::Mixin::Lockable]
9
+ def lock
10
+ meta['locked'] = 1
11
+ self
12
+ end
13
+
14
+ # Sames as #lock. Locks an object and immediately saves it.
15
+ #
16
+ # @return [GoodData::Mixin::Lockable]
17
+ def lock!
18
+ lock
19
+ save
20
+ end
21
+
22
+ # Unlocks an object.
23
+ #
24
+ # @return [GoodData::Mixin::Lockable]
25
+ def unlock
26
+ meta.delete('locked')
27
+ self
28
+ end
29
+
30
+ # Same as #unlock. Unlocks an object and immediately saves it
31
+ #
32
+ # @return [GoodData::Mixin::Lockable]
33
+ def unlock!
34
+ unlock
35
+ save
36
+ end
37
+
38
+ # Locks an object with all used objects. The types of objects that are affected by locks
39
+ # are dashboards, reports and metrics. This means that if you lock a dashboard by this method
40
+ # all used reports and metrics are also locked. If you lock a report all used metrics are also
41
+ # locked. The current object is reloaded. This means that the #locked? will return true.
42
+ #
43
+ # @return [GoodData::Mixin::Lockable]
44
+ def lock_with_dependencies!
45
+ client.post("/gdc/internal/projects/#{project.pid}/objects/setPermissions",
46
+ permissions: {
47
+ lock: true,
48
+ items: [uri]
49
+ })
50
+ reload!
51
+ end
52
+
53
+ # Unlocks an object with all used objects. The types of objects that are affected by locks
54
+ # are dashboards, reports and metrics. This means that if you unlock a dashboard by this method
55
+ # all used reports and metrics are also unlocked. If you unlock a report all used metrics are also
56
+ # unlocked. The current object is unlocked as well. Beware that certain objects might be in use in
57
+ # multiple contexts. For example one metric can be used in several reports. This method performs no
58
+ # checks to determine if an object should stay locked or not.
59
+ #
60
+ # @return [GoodData::Mixin::Lockable]
61
+ def unlock_with_dependencies!
62
+ using('report').pmap { |link| project.reports(link['link']) }.select(&:locked?).pmap(&:unlock!)
63
+ using('metric').pmap { |link| project.metrics(link['link']) }.select(&:locked?).pmap(&:unlock!)
64
+ using('projectDashboard').pmap { |link| project.dashboards(link['link']) }.select(&:locked?).pmap(&:unlock!)
65
+ unlock!
66
+ end
67
+
68
+ # Returns true if an object is locked. False otherwise.
69
+ #
70
+ # @return [Boolean]
71
+ def locked?
72
+ meta['locked'] == 1
73
+ end
74
+
75
+ # Returns true if an object is unlocked. False otherwise.
76
+ #
77
+ # @return [Boolean]
78
+ def unlocked?
79
+ !locked?
80
+ end
81
+ end
82
+ end
83
+ end
@@ -35,7 +35,7 @@ module GoodData
35
35
  fail 'Unexpected object id format: expected numeric ID, identifier with no slashes or an URI starting with a slash'
36
36
  end
37
37
  # new(GoodData.get uri) unless uri.nil?
38
- if uri
38
+ if uri # rubocop:disable Style/GuardClause
39
39
  raw = client.get(uri)
40
40
  client.create(self, raw, client: client, project: project)
41
41
  end
@@ -8,7 +8,7 @@ module GoodData
8
8
  # @param options [Hash] the options hash
9
9
  # @option options [Boolean] :full if passed true the subclass can decide to pull in full objects. This is desirable from the usability POV but unfortunately has negative impact on performance so it is not the default
10
10
  # @return [Array<GoodData::MdObject> | Array<Hash>] Return the appropriate metadata objects or their representation
11
- def all(options = { :client => GoodData.connection, :project => GoodData.project })
11
+ def all(_options = { :client => GoodData.connection, :project => GoodData.project })
12
12
  fail NotImplementedError, 'Method should be implemented in subclass. Currently there is no way how to get all metadata objects on API.'
13
13
  end
14
14
 
@@ -14,12 +14,6 @@ module GoodData
14
14
 
15
15
  # Returns which objects uses this MD resource
16
16
  def usedby(key = nil, opts = { :client => client, :project => project })
17
- p = opts[:project]
18
- fail ArgumentError, 'No :project specified' if p.nil?
19
-
20
- project = GoodData::Project[p, opts]
21
- fail ArgumentError, 'Wrong :project specified' if project.nil?
22
-
23
17
  dependency("#{project.md['usedby2']}/#{obj_id}", key, opts)
24
18
  end
25
19
 
@@ -27,9 +21,6 @@ module GoodData
27
21
 
28
22
  # Returns which objects this MD resource uses
29
23
  def using(key = nil, opts = { :client => client, :project => project })
30
- project = opts[:project]
31
- fail ArgumentError, 'Wrong :project specified' if project.nil?
32
-
33
24
  dependency("#{project.md['using2']}/#{obj_id}", key, opts)
34
25
  end
35
26
 
@@ -18,7 +18,7 @@ module GoodData
18
18
  def to_hash
19
19
  {
20
20
  :name => @name,
21
- :tabs => @tabs.map { |tab| tab.to_hash }
21
+ :tabs => @tabs.map(&:to_hash)
22
22
  }
23
23
  end
24
24
  end
@@ -43,9 +43,9 @@ module GoodData
43
43
  # Optional authentication modes
44
44
  tmp = opts[:authentication_modes]
45
45
  if tmp
46
- if tmp.kind_of? Array
46
+ if tmp.is_a? Array
47
47
  data[:authenticationModes] = tmp
48
- elsif tmp.kind_of? String
48
+ elsif tmp.is_a? String
49
49
  data[:authenticationModes] = [tmp]
50
50
  end
51
51
  end