gooddata 0.6.0 → 0.6.2

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 (208) hide show
  1. checksums.yaml +13 -5
  2. data/.rubocop.yml +23 -0
  3. data/.travis.yml +9 -4
  4. data/CLI.md +439 -0
  5. data/Gemfile +0 -1
  6. data/README.md +2 -2
  7. data/Rakefile +60 -8
  8. data/doc/templates/default/module/setup.rb +1 -1
  9. data/examples.rb +2 -0
  10. data/gooddata +2 -0
  11. data/gooddata.gemspec +12 -8
  12. data/lib/gooddata.rb +0 -2
  13. data/lib/gooddata/bricks/base_downloader.rb +52 -47
  14. data/lib/gooddata/bricks/brick.rb +20 -31
  15. data/lib/gooddata/bricks/bricks.rb +1 -1
  16. data/lib/gooddata/bricks/middleware/base_middleware.rb +9 -7
  17. data/lib/gooddata/bricks/middleware/bench_middleware.rb +12 -10
  18. data/lib/gooddata/bricks/middleware/bulk_salesforce_middleware.rb +28 -28
  19. data/lib/gooddata/bricks/middleware/fs_upload_middleware.rb +20 -16
  20. data/lib/gooddata/bricks/middleware/gooddata_middleware.rb +21 -19
  21. data/lib/gooddata/bricks/middleware/logger_middleware.rb +10 -8
  22. data/lib/gooddata/bricks/middleware/restforce_middleware.rb +36 -34
  23. data/lib/gooddata/bricks/middleware/stdout_middleware.rb +11 -9
  24. data/lib/gooddata/bricks/middleware/twitter_middleware.rb +14 -12
  25. data/lib/gooddata/bricks/pipeline.rb +28 -0
  26. data/lib/gooddata/bricks/utils.rb +10 -8
  27. data/lib/gooddata/cli/cli.rb +1 -6
  28. data/lib/gooddata/cli/commands/auth_cmd.rb +1 -1
  29. data/lib/gooddata/cli/commands/console_cmd.rb +7 -5
  30. data/lib/gooddata/cli/commands/domain_cmd.rb +45 -0
  31. data/lib/gooddata/cli/commands/process_cmd.rb +42 -5
  32. data/lib/gooddata/cli/commands/project_cmd.rb +96 -36
  33. data/lib/gooddata/cli/commands/projects_cmd.rb +21 -0
  34. data/lib/gooddata/cli/commands/role_cmd.rb +28 -0
  35. data/lib/gooddata/cli/commands/run_ruby_cmd.rb +5 -5
  36. data/lib/gooddata/cli/commands/scaffold_cmd.rb +1 -1
  37. data/lib/gooddata/cli/commands/{profile_cmd.rb → user_cmd.rb} +7 -9
  38. data/lib/gooddata/cli/shared.rb +3 -2
  39. data/lib/gooddata/client.rb +16 -304
  40. data/lib/gooddata/commands/api.rb +13 -5
  41. data/lib/gooddata/commands/auth.rb +47 -40
  42. data/lib/gooddata/commands/base.rb +4 -2
  43. data/lib/gooddata/commands/commands.rb +1 -1
  44. data/lib/gooddata/commands/datasets.rb +20 -7
  45. data/lib/gooddata/commands/domain.rb +23 -0
  46. data/lib/gooddata/commands/process.rb +23 -117
  47. data/lib/gooddata/commands/project.rb +147 -0
  48. data/lib/gooddata/commands/projects.rb +8 -102
  49. data/lib/gooddata/commands/role.rb +26 -0
  50. data/lib/gooddata/commands/runners.rb +41 -38
  51. data/lib/gooddata/commands/scaffold.rb +46 -43
  52. data/lib/gooddata/commands/user.rb +33 -0
  53. data/lib/gooddata/connection.rb +43 -353
  54. data/lib/gooddata/core/connection.rb +389 -0
  55. data/lib/gooddata/core/core.rb +5 -4
  56. data/lib/gooddata/core/logging.rb +48 -0
  57. data/lib/gooddata/core/nil_logger.rb +13 -0
  58. data/lib/gooddata/core/project.rb +70 -0
  59. data/lib/gooddata/core/rest.rb +120 -0
  60. data/lib/gooddata/core/threaded.rb +14 -0
  61. data/lib/gooddata/core/user.rb +19 -0
  62. data/lib/gooddata/data/data.rb +2 -1
  63. data/lib/gooddata/data/guesser.rb +16 -12
  64. data/lib/gooddata/exceptions/command_failed.rb +1 -1
  65. data/lib/gooddata/exceptions/exceptions.rb +2 -1
  66. data/lib/gooddata/exceptions/no_project_error.rb +11 -0
  67. data/lib/gooddata/exceptions/project_not_found.rb +1 -1
  68. data/lib/gooddata/extensions/big_decimal.rb +6 -2
  69. data/lib/gooddata/extract.rb +10 -8
  70. data/lib/gooddata/goodzilla/goodzilla.rb +61 -59
  71. data/lib/gooddata/helpers.rb +15 -9
  72. data/lib/gooddata/models/account_settings.rb +124 -0
  73. data/lib/gooddata/models/attributes/anchor.rb +37 -0
  74. data/lib/gooddata/models/attributes/attributes.rb +8 -0
  75. data/lib/gooddata/models/attributes/date_attribute.rb +25 -0
  76. data/lib/gooddata/models/attributes/time_attribute.rb +24 -0
  77. data/lib/gooddata/models/columns/attribute.rb +71 -0
  78. data/lib/gooddata/models/columns/columns.rb +8 -0
  79. data/lib/gooddata/models/columns/date_column.rb +63 -0
  80. data/lib/gooddata/models/columns/fact_model.rb +54 -0
  81. data/lib/gooddata/models/columns/label.rb +55 -0
  82. data/lib/gooddata/models/columns/reference.rb +57 -0
  83. data/lib/gooddata/models/dashboard_builder.rb +26 -0
  84. data/lib/gooddata/models/data_result.rb +10 -9
  85. data/lib/gooddata/models/domain.rb +131 -0
  86. data/lib/gooddata/models/empty_result.rb +5 -8
  87. data/lib/gooddata/models/facts/facts.rb +8 -0
  88. data/lib/gooddata/models/facts/time_fact.rb +20 -0
  89. data/lib/gooddata/models/folders/attribute_folder.rb +20 -0
  90. data/lib/gooddata/models/folders/fact_folder.rb +20 -0
  91. data/lib/gooddata/models/folders/folders.rb +8 -0
  92. data/lib/gooddata/models/invitation.rb +78 -0
  93. data/lib/gooddata/models/links.rb +6 -6
  94. data/lib/gooddata/models/md_object.rb +25 -0
  95. data/lib/gooddata/models/metadata.rb +160 -62
  96. data/lib/gooddata/models/metadata/attribute.rb +81 -0
  97. data/lib/gooddata/models/metadata/column.rb +61 -0
  98. data/lib/gooddata/models/{dashboard.rb → metadata/dashboard.rb} +12 -7
  99. data/lib/gooddata/models/{data_set.rb → metadata/data_set.rb} +5 -4
  100. data/lib/gooddata/models/metadata/date_dimension.rb +26 -0
  101. data/lib/gooddata/models/metadata/display_form.rb +61 -0
  102. data/lib/gooddata/models/metadata/fact.rb +36 -0
  103. data/lib/gooddata/models/metadata/folder.rb +24 -0
  104. data/lib/gooddata/models/metadata/metadata.rb +8 -0
  105. data/lib/gooddata/models/metadata/metric.rb +197 -0
  106. data/lib/gooddata/models/metadata/report.rb +115 -0
  107. data/lib/gooddata/models/{report_definition.rb → metadata/report_definition.rb} +16 -10
  108. data/lib/gooddata/models/metadata/schema.rb +227 -0
  109. data/lib/gooddata/models/model.rb +38 -1339
  110. data/lib/gooddata/models/models.rb +5 -2
  111. data/lib/gooddata/models/module_constants.rb +29 -0
  112. data/lib/gooddata/models/process.rb +142 -13
  113. data/lib/gooddata/models/profile.rb +4 -6
  114. data/lib/gooddata/models/project.rb +406 -136
  115. data/lib/gooddata/models/project_blueprint.rb +221 -0
  116. data/lib/gooddata/models/project_builder.rb +136 -0
  117. data/lib/gooddata/models/project_creator.rb +138 -0
  118. data/lib/gooddata/models/project_metadata.rb +11 -10
  119. data/lib/gooddata/models/project_role.rb +92 -0
  120. data/lib/gooddata/models/references/date_reference.rb +44 -0
  121. data/lib/gooddata/models/references/references.rb +8 -0
  122. data/lib/gooddata/models/references/time_reference.rb +13 -0
  123. data/lib/gooddata/models/report_data_result.rb +11 -11
  124. data/lib/gooddata/models/schedule.rb +284 -0
  125. data/lib/gooddata/models/schema_blueprint.rb +158 -0
  126. data/lib/gooddata/models/schema_builder.rb +81 -0
  127. data/lib/gooddata/models/tab_builder.rb +23 -0
  128. data/lib/gooddata/models/user.rb +165 -0
  129. data/lib/gooddata/version.rb +1 -1
  130. data/lib/templates/project/data/devs.csv +1 -1
  131. data/lib/templates/project/data/repos.csv +1 -1
  132. data/lib/templates/project/model/model.rb.erb +7 -11
  133. data/spec/bricks/bricks_spec.rb +2 -0
  134. data/spec/data/test-ci-data.csv +2 -0
  135. data/spec/data/test_project_model_spec.json +7 -27
  136. data/spec/helpers/blueprint_helper.rb +2 -0
  137. data/spec/helpers/cli_helper.rb +2 -0
  138. data/spec/helpers/connection_helper.rb +14 -1
  139. data/spec/helpers/project_helper.rb +16 -0
  140. data/spec/helpers/schema_helper.rb +16 -0
  141. data/spec/integration/command_projects_spec.rb +7 -7
  142. data/spec/integration/create_from_template_spec.rb +2 -2
  143. data/spec/integration/full_project_spec.rb +160 -7
  144. data/spec/integration/partial_md_export_import_spec.rb +3 -3
  145. data/spec/logging_in_logging_out_spec.rb +2 -1
  146. data/spec/spec_helper.rb +26 -4
  147. data/spec/unit/bricks/bricks_spec.rb +15 -7
  148. data/spec/unit/bricks/middleware/bench_middleware_spec.rb +2 -0
  149. data/spec/unit/bricks/middleware/bulk_salesforce_middleware_spec.rb +2 -0
  150. data/spec/unit/bricks/middleware/gooddata_middleware_spec.rb +2 -0
  151. data/spec/unit/bricks/middleware/logger_middleware_spec.rb +2 -0
  152. data/spec/unit/bricks/middleware/restforce_middleware_spec.rb +2 -0
  153. data/spec/unit/bricks/middleware/stdout_middleware_spec.rb +2 -0
  154. data/spec/unit/bricks/middleware/twitter_middleware_spec.rb +2 -0
  155. data/spec/unit/cli/cli_spec.rb +2 -0
  156. data/spec/unit/cli/commands/cmd_api_spec.rb +23 -15
  157. data/spec/unit/cli/commands/cmd_auth_spec.rb +8 -4
  158. data/spec/unit/cli/commands/cmd_domain_spec.rb +82 -0
  159. data/spec/unit/cli/commands/cmd_process_spec.rb +29 -13
  160. data/spec/unit/cli/commands/cmd_project_spec.rb +51 -30
  161. data/spec/unit/cli/commands/cmd_role_spec.rb +44 -0
  162. data/spec/unit/cli/commands/cmd_run_ruby_spec.rb +8 -4
  163. data/spec/unit/cli/commands/cmd_scaffold_spec.rb +48 -11
  164. data/spec/unit/cli/commands/cmd_user_spec.rb +29 -0
  165. data/spec/unit/commands/command_api_spec.rb +1 -1
  166. data/spec/unit/commands/command_auth_spec.rb +100 -18
  167. data/spec/unit/commands/command_dataset_spec.rb +4 -0
  168. data/spec/unit/commands/command_process_spec.rb +9 -4
  169. data/spec/unit/commands/command_projects_spec.rb +10 -6
  170. data/spec/unit/commands/command_scaffold_spec.rb +5 -1
  171. data/spec/unit/commands/command_user_spec.rb +22 -0
  172. data/spec/unit/core/connection_spec.rb +35 -6
  173. data/spec/unit/core/logging_spec.rb +65 -0
  174. data/spec/unit/core/nil_logger_spec.rb +9 -0
  175. data/spec/unit/core/project_spec.rb +51 -0
  176. data/spec/unit/core/rest_spec.rb +33 -0
  177. data/spec/unit/data/guesser_spec.rb +5 -0
  178. data/spec/unit/godzilla/goodzilla_spec.rb +2 -0
  179. data/spec/unit/models/account_settings_spec.rb +28 -0
  180. data/spec/unit/models/anchor_spec.rb +32 -0
  181. data/spec/unit/models/attribute_column_spec.rb +7 -0
  182. data/spec/unit/models/domain_spec.rb +45 -0
  183. data/spec/unit/models/invitation_spec.rb +13 -0
  184. data/spec/unit/models/md_object_spec.rb +47 -0
  185. data/spec/unit/models/metric.rb +92 -0
  186. data/spec/unit/{model → models}/model_spec.rb +9 -7
  187. data/spec/unit/models/project_blueprint_spec.rb +202 -0
  188. data/spec/unit/models/project_creator.rb +73 -0
  189. data/spec/unit/models/project_role_spec.rb +90 -0
  190. data/spec/unit/models/project_spec.rb +143 -0
  191. data/spec/unit/models/schedule_spec.rb +491 -0
  192. data/spec/unit/{model → models}/schema_builder_spec.rb +2 -0
  193. data/spec/unit/{model → models}/tools_spec.rb +13 -7
  194. data/spec/unit/models/user_spec.rb +16 -0
  195. data/test/test_upload.rb +2 -0
  196. metadata +189 -86
  197. data/lib/gooddata/commands/profile.rb +0 -11
  198. data/lib/gooddata/models/attribute.rb +0 -29
  199. data/lib/gooddata/models/display_form.rb +0 -9
  200. data/lib/gooddata/models/fact.rb +0 -19
  201. data/lib/gooddata/models/metric.rb +0 -99
  202. data/lib/gooddata/models/report.rb +0 -89
  203. data/spec/data/blueprint_valid.json +0 -37
  204. data/spec/unit/cli/commands/cmd_profile_spec.rb +0 -16
  205. data/spec/unit/commands/command_profile_spec.rb +0 -18
  206. data/spec/unit/core/core_spec.rb +0 -7
  207. data/spec/unit/model/blueprint_spec.rb +0 -132
  208. data/spec/unit/model/project_blueprint_spec.rb +0 -44
@@ -0,0 +1,221 @@
1
+ # encoding: UTF-8
2
+
3
+ module GoodData
4
+ module Model
5
+ class ProjectBlueprint
6
+ attr_accessor :data
7
+
8
+ def self.from_json(spec)
9
+ if spec.is_a?(String)
10
+ ProjectBlueprint.new(MultiJson.load(File.read(spec), :symbolize_keys => true))
11
+ else
12
+ ProjectBlueprint.new(spec)
13
+ end
14
+ end
15
+
16
+ def change(&block)
17
+ builder = ProjectBuilder.create_from_data(self)
18
+ block.call(builder)
19
+ @data = builder.to_hash
20
+ self
21
+ end
22
+
23
+ def datasets
24
+ data[:datasets].map { |d| DatasetBlueprint.new(d) }
25
+ end
26
+
27
+ def add_dataset(a_dataset, index = nil)
28
+ if index.nil? || index > datasets.length
29
+ data[:datasets] << a_dataset.to_hash
30
+ else
31
+ data[:datasets].insert(index, a_dataset.to_hash)
32
+ end
33
+ end
34
+
35
+ def remove_dataset(dataset_name)
36
+ dataset = dataset_name.is_a?(String) ? find_dataset(dataset_name) : dataset_name
37
+ index = data[:datasets].index(dataset)
38
+ data[:datasets].delete_at(index)
39
+ end
40
+
41
+ def date_dimensions
42
+ data[:date_dimensions]
43
+ end
44
+
45
+ def dataset?(name)
46
+ found = data[:datasets].find { |d| d[:name] == name }
47
+ found != nil
48
+ end
49
+
50
+ def find_dataset(name)
51
+ ds = data[:datasets].find { |d| d[:name] == name }
52
+ fail "Dataset #{name} could not be found" if ds.nil?
53
+ DatasetBlueprint.new(ds)
54
+ end
55
+
56
+ def initialize(init_data)
57
+ @data = init_data
58
+ end
59
+
60
+ def validate_references
61
+ if datasets.count == 1
62
+ []
63
+ else
64
+ x = datasets.reduce([]) { |a, e| e.anchor? ? a << [e.name, e.anchor[:name]] : a }
65
+ refs = datasets.reduce([]) do |a, e|
66
+ a.concat(e.references)
67
+ end
68
+ refs.reduce([]) do |a, e|
69
+ x.include?([e[:dataset], e[:reference]]) ? a : a.concat([e])
70
+ end
71
+ end
72
+ end
73
+
74
+ def validate_labels_references
75
+ datasets.reduce([]) { |a, e| a.concat(e.validate_label_references) }
76
+ end
77
+
78
+ def validate_model
79
+ refs_errors = validate_references
80
+ labels_errors = validate_labels_references
81
+ refs_errors.concat(labels_errors)
82
+ end
83
+
84
+ def model_valid?
85
+ refs_errors = validate_references
86
+ labels_errors = validate_labels_references
87
+ errors = refs_errors.concat(labels_errors)
88
+ errors.empty? ? true : false
89
+ end
90
+
91
+ def referenced_by(dataset)
92
+ dataset = find_dataset(dataset) if dataset.is_a?(String)
93
+ dataset.references.map do |ds|
94
+ find_dataset(ds[:dataset])
95
+ end
96
+ end
97
+
98
+ def attributes
99
+ datasets.reduce([]) { |a, e| a.concat(e.attributes) }
100
+ end
101
+
102
+ def attributes_and_anchors
103
+ datasets.reduce([]) { |a, e| a.concat(e.attributes_and_anchors) }
104
+ end
105
+
106
+ def labels
107
+ datasets.reduce([]) { |a, e| a.concat(e.labels) }
108
+ end
109
+
110
+ def facts
111
+ datasets.reduce([]) { |a, e| a.concat(e.facts) }
112
+ end
113
+
114
+ def can_break(dataset)
115
+ dataset = find_dataset(dataset) if dataset.is_a?(String)
116
+ referenced_by(dataset).reduce([]) do |a, e|
117
+ e.attributes_and_anchors.each do |attr|
118
+ a.push([e, attr])
119
+ end
120
+ a
121
+ end
122
+ end
123
+
124
+ def find_star_centers
125
+ referenced = datasets.map { |d| referenced_by(d) }
126
+ referenced.flatten!
127
+ res = datasets.map(&:to_hash) - referenced.map(&:to_hash)
128
+ res.map { |d| DatasetBlueprint.new(d) }
129
+ end
130
+
131
+ def suggest_reports(options = {})
132
+ strategy = options[:strategy] || :stupid
133
+ case strategy
134
+ when :stupid
135
+ reports = suggest_metrics.reduce([]) do |a, e|
136
+ star, metrics = e
137
+ metrics.each { |m| m.save }
138
+ reports_stubs = metrics.map do |m|
139
+ breaks = can_break(star).map { |ds, aM| ds.identifier_for(aM) }
140
+ # [breaks.sample((breaks.length/10.0).ceil), m]
141
+ [breaks, m]
142
+ end
143
+ a.concat(reports_stubs)
144
+ end
145
+ reports.reduce([]) do |a, e|
146
+ attrs, metric = e
147
+
148
+ attrs.each do |attr|
149
+ a << GoodData::Report.create(
150
+ :title => 'Fantastic report',
151
+ :top => [attr],
152
+ :left => metric)
153
+ end
154
+ a
155
+ end
156
+ end
157
+ end
158
+
159
+ def suggest_metrics
160
+ stars = find_star_centers
161
+ metrics = stars.map { |s| s.suggest_metrics }
162
+ stars.zip(metrics)
163
+ end
164
+
165
+ def merge!(a_blueprint)
166
+ temp_blueprint = merge(a_blueprint)
167
+ @data = temp_blueprint.data
168
+ self
169
+ end
170
+
171
+ def merge(a_blueprint)
172
+ temp_blueprint = dup
173
+ a_blueprint.datasets.each do |dataset|
174
+ if temp_blueprint.dataset?(dataset.name)
175
+ local_dataset = temp_blueprint.find_dataset(dataset.name)
176
+ index = temp_blueprint.datasets.index(local_dataset)
177
+ local_dataset.merge!(dataset)
178
+ temp_blueprint.remove_dataset(local_dataset.name)
179
+ temp_blueprint.add_dataset(local_dataset, index)
180
+ else
181
+ temp_blueprint.add_dataset(dataset.dup)
182
+ end
183
+ end
184
+ temp_blueprint
185
+ end
186
+
187
+ def dup
188
+ deep_copy = Marshal.load(Marshal.dump(data))
189
+ ProjectBlueprint.new(deep_copy)
190
+ end
191
+
192
+ def title
193
+ data[:title]
194
+ end
195
+
196
+ def to_wire_model
197
+ {
198
+ 'diffRequest' => {
199
+ 'targetModel' => {
200
+ 'projectModel' => {
201
+ 'datasets' => datasets.map { |d| d.to_wire_model },
202
+ 'dateDimensions' => date_dimensions.map do |d|
203
+ {
204
+ 'dateDimension' => {
205
+ 'name' => d[:name],
206
+ 'title' => d[:title] || d[:name].humanize
207
+ }
208
+ }
209
+ end
210
+ }
211
+ }
212
+ }
213
+ }
214
+ end
215
+
216
+ def to_hash
217
+ @data
218
+ end
219
+ end
220
+ end
221
+ end
@@ -0,0 +1,136 @@
1
+ # encoding: UTF-8
2
+
3
+ require_relative 'dashboard_builder'
4
+ require_relative 'schema_builder'
5
+
6
+ module GoodData
7
+ module Model
8
+ class ProjectBuilder
9
+ attr_reader :title, :datasets, :reports, :metrics, :uploads, :users, :assert_report, :date_dimensions
10
+
11
+ class << self
12
+ def create_from_data(blueprint, title = 'Title')
13
+ pb = ProjectBuilder.new(title)
14
+ pb.data = blueprint.to_hash
15
+ pb
16
+ end
17
+
18
+ def create(title, options = {}, &block)
19
+ pb = ProjectBuilder.new(title)
20
+ block.call(pb)
21
+ pb
22
+ end
23
+ end
24
+
25
+ def initialize(title)
26
+ @title = title
27
+ @datasets = []
28
+ @reports = []
29
+ @assert_tests = []
30
+ @metrics = []
31
+ @uploads = []
32
+ @users = []
33
+ @dashboards = []
34
+ @date_dimensions = []
35
+ end
36
+
37
+ def add_date_dimension(name, options = {})
38
+ dimension = {
39
+ urn: options[:urn],
40
+ name: name,
41
+ title: options[:title]
42
+ }
43
+
44
+ @date_dimensions << dimension
45
+ end
46
+
47
+ def add_dataset(name, &block)
48
+ builder = GoodData::Model::SchemaBuilder.new(name)
49
+ block.call(builder)
50
+ if @datasets.any? { |item| item[:name] == name }
51
+ ds = @datasets.find { |item| item[:name] == name }
52
+ index = @datasets.index(ds)
53
+ stuff = GoodData::Model.merge_dataset_columns(ds, builder.to_hash)
54
+ @datasets.delete_at(index)
55
+ @datasets.insert(index, stuff)
56
+ else
57
+ @datasets << builder.to_hash
58
+ end
59
+ end
60
+
61
+ def add_report(title, options = {})
62
+ @reports << { :title => title }.merge(options)
63
+ end
64
+
65
+ def add_metric(title, options = {})
66
+ @metrics << { :title => title }.merge(options)
67
+ end
68
+
69
+ def add_dashboard(title, &block)
70
+ db = DashboardBuilder.new(title)
71
+ block.call(db)
72
+ @dashboards << db.to_hash
73
+ end
74
+
75
+ def load_metrics(file)
76
+ new_metrics = MultiJson.load(open(file).read, :symbolize_keys => true)
77
+ @metrics += new_metrics
78
+ end
79
+
80
+ def load_datasets(file)
81
+ new_metrics = MultiJson.load(open(file).read, :symbolize_keys => true)
82
+ @datasets += new_metrics
83
+ end
84
+
85
+ def assert_report(report, result)
86
+ @assert_tests << { :report => report, :result => result }
87
+ end
88
+
89
+ def upload(data, options = {})
90
+ mode = options[:mode] || 'FULL'
91
+ dataset = options[:dataset]
92
+ @uploads << {
93
+ :source => data,
94
+ :mode => mode,
95
+ :dataset => dataset
96
+ }
97
+ end
98
+
99
+ def add_users(users)
100
+ @users << users
101
+ end
102
+
103
+ def to_json(options = {})
104
+ eliminate_empty = options[:eliminate_empty] || false
105
+
106
+ if eliminate_empty
107
+ JSON.pretty_generate(to_hash.reject { |k, v| v.is_a?(Enumerable) && v.empty? })
108
+ else
109
+ JSON.pretty_generate(to_hash)
110
+ end
111
+ end
112
+
113
+ def to_blueprint
114
+ GoodData::Model::ProjectBlueprint.new(to_hash)
115
+ end
116
+
117
+ def to_hash
118
+ {
119
+ :title => @title,
120
+ :datasets => @datasets,
121
+ :uploads => @uploads,
122
+ :dashboards => @dashboards,
123
+ :metrics => @metrics,
124
+ :reports => @reports,
125
+ :users => @users,
126
+ :assert_tests => @assert_tests,
127
+ :date_dimensions => @date_dimensions
128
+ }
129
+ end
130
+
131
+ def find_dataset(name)
132
+ datasets.find { |d| d.name == name }
133
+ end
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,138 @@
1
+ # encoding: UTF-8
2
+
3
+ require_relative 'project'
4
+ require_relative 'project_blueprint'
5
+
6
+ require 'open-uri'
7
+
8
+ module GoodData
9
+ module Model
10
+ class ProjectCreator
11
+ class << self
12
+ def migrate(options = {})
13
+ spec = options[:spec] || fail('You need to provide spec for migration')
14
+ spec = spec.to_hash
15
+
16
+ token = options[:token]
17
+ project = options[:project] || GoodData::Project.create(:title => spec[:title], :auth_token => token)
18
+ fail('You need to specify token for project creation') if token.nil? && project.nil?
19
+
20
+ begin
21
+ GoodData.with_project(project) do |p|
22
+ # migrate_date_dimensions(p, spec[:date_dimensions] || [])
23
+ migrate_datasets(p, spec)
24
+ load(p, spec)
25
+ migrate_metrics(p, spec[:metrics] || [])
26
+ migrate_reports(p, spec[:reports] || [])
27
+ migrate_dashboards(p, spec[:dashboards] || [])
28
+ migrate_users(p, spec[:users] || [])
29
+ execute_tests(p, spec[:assert_tests] || [])
30
+ p
31
+ end
32
+ end
33
+ end
34
+
35
+ def migrate_date_dimensions(project, spec)
36
+ spec.each do |dd|
37
+ Model.add_schema(DateDimension.new(dd), project)
38
+ end
39
+ end
40
+
41
+ def migrate_datasets(project, spec)
42
+ bp = ProjectBlueprint.new(spec)
43
+ # schema = Schema.load(schema) unless schema.respond_to?(:to_maql_create)
44
+ # project = GoodData.project unless project
45
+ uri = "/gdc/projects/#{GoodData.project.pid}/model/diff"
46
+ result = GoodData.post(uri, bp.to_wire_model)
47
+ link = result['asyncTask']['link']['poll']
48
+ response = GoodData.get(link, :process => false)
49
+ # pp response
50
+ while response.code != 200
51
+ sleep 1
52
+ GoodData.connection.retryable(:tries => 3, :on => RestClient::InternalServerError) do
53
+ sleep 1
54
+ response = GoodData.get(link, :process => false)
55
+ # pp response
56
+ end
57
+ end
58
+ response = GoodData.get(link)
59
+ ldm_links = GoodData.get project.md[LDM_CTG]
60
+ ldm_uri = Links.new(ldm_links)[LDM_MANAGE_CTG]
61
+ chunks = pick_correct_chunks(response['projectModelDiff']['updateScripts'])
62
+ chunks['updateScript']['maqlDdlChunks'].each do |chunk|
63
+ response = GoodData.post ldm_uri, 'manage' => { 'maql' => chunk }
64
+ polling_url = response['entries'].first['link']
65
+
66
+ polling_result = GoodData.get(polling_url)
67
+ while polling_result['wTaskStatus']['status'] == 'RUNNING'
68
+ sleep(3)
69
+ polling_result = GoodData.get(polling_url)
70
+ end
71
+ fail 'Creating dataset failed' if polling_result['wTaskStatus']['status'] == 'ERROR'
72
+
73
+ end
74
+
75
+ bp.datasets.each do |ds|
76
+ schema = ds.to_schema
77
+ GoodData::ProjectMetadata["manifest_#{schema.name}"] = schema.to_manifest.to_json
78
+ end
79
+ end
80
+
81
+ def migrate_reports(project, spec)
82
+ spec.each do |report|
83
+ project.add_report(report)
84
+ end
85
+ end
86
+
87
+ def migrate_dashboards(project, spec)
88
+ spec.each do |dash|
89
+ project.add_dashboard(dash)
90
+ end
91
+ end
92
+
93
+ def migrate_metrics(project, spec)
94
+ spec.each do |metric|
95
+ project.add_metric(metric)
96
+ end
97
+ end
98
+
99
+ def migrate_users(project, spec)
100
+ spec.each do |user|
101
+ puts "Would migrate user #{user}"
102
+ # project.add_user(user)
103
+ end
104
+ end
105
+
106
+ def load(project, spec)
107
+ if spec.key?(:uploads)
108
+ spec[:uploads].each do |load|
109
+ schema = GoodData::Model::Schema.new(spec[:datasets].find { |d| d[:name] == load[:dataset] })
110
+ project.upload(load[:source], schema, load[:mode])
111
+ end
112
+ end
113
+ end
114
+
115
+ def execute_tests(project, spec)
116
+ spec.each do |assert|
117
+ result = GoodData::ReportDefinition.execute(assert[:report])
118
+ fail "Test did not pass. Got #{result.table.inspect}, expected #{assert[:result].inspect}" if result.table != assert[:result]
119
+ end
120
+ end
121
+
122
+ def pick_correct_chunks(chunks)
123
+ # first is cascadeDrops, second is preserveData
124
+ rules = [
125
+ [false, true],
126
+ [false, false],
127
+ [true, true],
128
+ [true, false]
129
+ ]
130
+ stuff = chunks.select { |chunk| chunk['updateScript']['maqlDdlChunks'] }
131
+ rules.reduce(nil) do |a, e|
132
+ a || stuff.find { |chunk| e[0] == chunk['updateScript']['cascadeDrops'] && e[1] == chunk['updateScript']['preserveData'] }
133
+ end
134
+ end
135
+ end
136
+ end
137
+ end
138
+ end