gooddata 0.6.18 → 0.6.19

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 (133) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -1
  3. data/.travis.yml +8 -19
  4. data/Guardfile +5 -0
  5. data/README.md +1 -3
  6. data/bin/gooddata +1 -1
  7. data/gooddata.gemspec +6 -4
  8. data/lib/gooddata.rb +1 -1
  9. data/lib/gooddata/bricks/middleware/aws_middleware.rb +24 -0
  10. data/lib/gooddata/cli/commands/console_cmd.rb +1 -1
  11. data/lib/gooddata/cli/commands/project_cmd.rb +29 -9
  12. data/lib/gooddata/cli/hooks.rb +9 -3
  13. data/lib/gooddata/commands/datawarehouse.rb +1 -7
  14. data/lib/gooddata/commands/project.rb +4 -3
  15. data/lib/gooddata/core/logging.rb +14 -2
  16. data/lib/gooddata/exceptions/execution_limit_exceeded.rb +9 -0
  17. data/lib/gooddata/exceptions/uncomputable_report.rb +8 -0
  18. data/lib/gooddata/exceptions/validation_error.rb +1 -1
  19. data/lib/gooddata/goodzilla/goodzilla.rb +5 -1
  20. data/lib/gooddata/helpers/data_helper.rb +40 -9
  21. data/lib/gooddata/mixins/md_finders.rb +35 -0
  22. data/lib/gooddata/models/blueprint/anchor_field.rb +46 -0
  23. data/lib/gooddata/models/blueprint/attribute_field.rb +25 -0
  24. data/lib/gooddata/models/blueprint/blueprint.rb +7 -0
  25. data/lib/gooddata/models/blueprint/blueprint_field.rb +66 -0
  26. data/lib/gooddata/models/{dashboard_builder.rb → blueprint/dashboard_builder.rb} +0 -0
  27. data/lib/gooddata/models/{schema_blueprint.rb → blueprint/dataset_blueprint.rb} +176 -117
  28. data/lib/gooddata/models/blueprint/date_dimension.rb +10 -0
  29. data/lib/gooddata/models/blueprint/fact_field.rb +16 -0
  30. data/lib/gooddata/models/blueprint/label_field.rb +39 -0
  31. data/lib/gooddata/models/{project_blueprint.rb → blueprint/project_blueprint.rb} +366 -168
  32. data/lib/gooddata/models/blueprint/project_builder.rb +79 -0
  33. data/lib/gooddata/models/blueprint/reference_field.rb +39 -0
  34. data/lib/gooddata/models/blueprint/schema_blueprint.rb +156 -0
  35. data/lib/gooddata/models/blueprint/schema_builder.rb +85 -0
  36. data/lib/gooddata/models/{to_manifest.rb → blueprint/to_manifest.rb} +25 -20
  37. data/lib/gooddata/models/{to_wire.rb → blueprint/to_wire.rb} +33 -52
  38. data/lib/gooddata/models/datawarehouse.rb +2 -2
  39. data/lib/gooddata/models/domain.rb +3 -2
  40. data/lib/gooddata/models/execution.rb +2 -2
  41. data/lib/gooddata/models/execution_detail.rb +7 -2
  42. data/lib/gooddata/models/from_wire.rb +60 -71
  43. data/lib/gooddata/models/from_wire_parse.rb +125 -125
  44. data/lib/gooddata/models/metadata.rb +14 -0
  45. data/lib/gooddata/models/metadata/dashboard.rb +2 -2
  46. data/lib/gooddata/models/metadata/label.rb +1 -1
  47. data/lib/gooddata/models/metadata/report.rb +6 -5
  48. data/lib/gooddata/models/metadata/report_definition.rb +44 -59
  49. data/lib/gooddata/models/model.rb +131 -43
  50. data/lib/gooddata/models/process.rb +13 -11
  51. data/lib/gooddata/models/profile.rb +12 -1
  52. data/lib/gooddata/models/project.rb +223 -19
  53. data/lib/gooddata/models/project_creator.rb +4 -15
  54. data/lib/gooddata/models/schedule.rb +1 -0
  55. data/lib/gooddata/models/user_filters/user_filter_builder.rb +2 -2
  56. data/lib/gooddata/rest/client.rb +18 -18
  57. data/lib/gooddata/rest/connection.rb +113 -94
  58. data/lib/gooddata/version.rb +1 -1
  59. data/lib/templates/project/model/model.rb.erb +15 -16
  60. data/spec/data/blueprints/additional_dataset_module.json +32 -0
  61. data/spec/data/blueprints/big_blueprint_not_pruned.json +2079 -0
  62. data/spec/data/blueprints/invalid_blueprint.json +103 -0
  63. data/spec/data/blueprints/m_n_model.json +104 -0
  64. data/spec/data/blueprints/model_module.json +25 -0
  65. data/spec/data/blueprints/test_blueprint.json +38 -0
  66. data/spec/data/blueprints/test_project_model_spec.json +106 -0
  67. data/spec/data/gd_gse_data_manifest.json +34 -34
  68. data/spec/data/manifests/test_blueprint.json +32 -0
  69. data/spec/data/{manifest_test_project.json → manifests/test_project.json} +9 -18
  70. data/spec/data/wire_models/test_blueprint.json +63 -0
  71. data/spec/data/wire_test_project.json +5 -5
  72. data/spec/environment/default.rb +33 -0
  73. data/spec/environment/develop.rb +26 -0
  74. data/spec/environment/environment.rb +14 -0
  75. data/spec/environment/hotfix.rb +17 -0
  76. data/spec/environment/production.rb +31 -0
  77. data/spec/environment/release.rb +17 -0
  78. data/spec/helpers/blueprint_helper.rb +10 -7
  79. data/spec/helpers/cli_helper.rb +24 -22
  80. data/spec/helpers/connection_helper.rb +27 -25
  81. data/spec/helpers/crypto_helper.rb +7 -5
  82. data/spec/helpers/csv_helper.rb +5 -3
  83. data/spec/helpers/process_helper.rb +15 -10
  84. data/spec/helpers/project_helper.rb +40 -33
  85. data/spec/helpers/schedule_helper.rb +15 -9
  86. data/spec/helpers/spec_helper.rb +11 -0
  87. data/spec/integration/blueprint_updates_spec.rb +93 -0
  88. data/spec/integration/command_datawarehouse_spec.rb +2 -1
  89. data/spec/integration/command_projects_spec.rb +9 -8
  90. data/spec/integration/create_from_template_spec.rb +1 -1
  91. data/spec/integration/create_project_spec.rb +1 -1
  92. data/spec/integration/full_process_schedule_spec.rb +1 -1
  93. data/spec/integration/full_project_spec.rb +91 -30
  94. data/spec/integration/over_to_user_filters_spec.rb +24 -28
  95. data/spec/integration/partial_md_export_import_spec.rb +4 -4
  96. data/spec/integration/project_spec.rb +1 -1
  97. data/spec/integration/rest_spec.rb +1 -1
  98. data/spec/integration/user_filters_spec.rb +19 -24
  99. data/spec/integration/variables_spec.rb +7 -9
  100. data/spec/logging_in_logging_out_spec.rb +1 -1
  101. data/spec/spec_helper.rb +10 -1
  102. data/spec/unit/bricks/middleware/aws_middelware_spec.rb +47 -0
  103. data/spec/unit/core/connection_spec.rb +2 -2
  104. data/spec/unit/core/logging_spec.rb +12 -4
  105. data/spec/unit/helpers/data_helper_spec.rb +60 -0
  106. data/spec/unit/models/blueprint/attributes_spec.rb +24 -0
  107. data/spec/unit/models/blueprint/dataset_spec.rb +116 -0
  108. data/spec/unit/models/blueprint/labels_spec.rb +39 -0
  109. data/spec/unit/models/blueprint/project_blueprint_spec.rb +643 -0
  110. data/spec/unit/models/blueprint/reference_spec.rb +24 -0
  111. data/spec/unit/models/{schema_builder_spec.rb → blueprint/schema_builder_spec.rb} +12 -4
  112. data/spec/unit/models/blueprint/to_wire_spec.rb +169 -0
  113. data/spec/unit/models/domain_spec.rb +13 -2
  114. data/spec/unit/models/from_wire_spec.rb +277 -98
  115. data/spec/unit/models/metadata_spec.rb +22 -4
  116. data/spec/unit/models/model_spec.rb +49 -39
  117. data/spec/unit/models/profile_spec.rb +1 -0
  118. data/spec/unit/models/project_spec.rb +7 -7
  119. data/spec/unit/models/schedule_spec.rb +20 -0
  120. data/spec/unit/models/to_manifest_spec.rb +31 -11
  121. data/spec/unit/rest/polling_spec.rb +86 -0
  122. metadata +102 -30
  123. data/lib/gooddata/models/project_builder.rb +0 -136
  124. data/lib/gooddata/models/schema_builder.rb +0 -77
  125. data/out.txt +0 -0
  126. data/spec/data/additional_dataset_module.json +0 -18
  127. data/spec/data/blueprint_invalid.json +0 -38
  128. data/spec/data/m_n_model/blueprint.json +0 -76
  129. data/spec/data/model_module.json +0 -18
  130. data/spec/data/test_project_model_spec.json +0 -76
  131. data/spec/unit/models/attribute_column_spec.rb +0 -7
  132. data/spec/unit/models/project_blueprint_spec.rb +0 -239
  133. data/spec/unit/models/to_wire_spec.rb +0 -71
@@ -0,0 +1,46 @@
1
+ # encoding: UTF-8
2
+
3
+ require_relative 'attribute_field'
4
+
5
+ module GoodData
6
+ module Model
7
+ class AnchorBlueprintField < AttributeBlueprintField
8
+ # Returns true if it is an anchor
9
+ #
10
+ # @return [Boolean] returns true
11
+ def anchor?
12
+ true
13
+ end
14
+
15
+ # Removes all the labels from the anchor. This is a typical operation that people want to perform
16
+ #
17
+ # @return [GoodData::Model::ProjectBlueprint] Returns changed blueprint
18
+ def strip!
19
+ dataset_blueprint.strip_anchor!
20
+ end
21
+
22
+ # Alias for strip!. Removes all the labels from the anchor. This is a typical operation that people want to perform
23
+ #
24
+ # @return [GoodData::Model::ProjectBlueprint] Returns changed blueprint
25
+ def remove!
26
+ strip!
27
+ end
28
+
29
+ # Returns labels for anchor or empty array if there are none
30
+ #
31
+ # @return [Array<GoodData::Model::LabelBlueprintField>] Returns list of labels
32
+ def labels
33
+ dataset_blueprint.labels_for_attribute(self)
34
+ end
35
+
36
+ # Validates the field for presence of mandatory fields
37
+ #
38
+ # @return [Array<Hash>] Returns list of errors as hashes
39
+ def validate
40
+ validate_presence_of(:id).map do |e|
41
+ { type: :error, message: "Field \"#{e}\" is not defined or empty for anchor \"#{id}\"" }
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,25 @@
1
+ # encoding: UTF-8
2
+
3
+ require_relative 'blueprint_field'
4
+
5
+ module GoodData
6
+ module Model
7
+ class AttributeBlueprintField < BlueprintField
8
+ # Returns list of labels on the attribute. There has to be always at least one attribute
9
+ #
10
+ # @return [Array] returns list of the errors represented by hash structures
11
+ def labels
12
+ @dataset_blueprint.labels_for_attribute(self)
13
+ end
14
+
15
+ # Validates the fields in the attribute
16
+ #
17
+ # @return [Array] returns list of the errors represented by hash structures
18
+ def validate
19
+ validate_presence_of(:id).map do |e|
20
+ { type: :error, message: "Field \"#{e}\" is not defined or empty for attribute \"#{id}\"" }
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,7 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'pathname'
4
+ base = Pathname(__FILE__).dirname.expand_path
5
+ Dir.glob(base + '*.rb').each do |file|
6
+ require file
7
+ end
@@ -0,0 +1,66 @@
1
+ # encoding: UTF-8
2
+
3
+ module GoodData
4
+ module Model
5
+ class BlueprintField
6
+ attr_reader :dataset_blueprint, :data
7
+
8
+ def id
9
+ @data[:id]
10
+ end
11
+
12
+ def initialize(data, dataset)
13
+ @data = data.symbolize_keys
14
+ @data[:type] = @data[:type].to_sym
15
+ @dataset_blueprint = dataset
16
+ end
17
+
18
+ # Returns the md object in associated project or throws error if not present
19
+ #
20
+ # @return [GoodData::MdObject] md object that is represented in the blueprint
21
+ def in_project(project)
22
+ GoodData::MdObject[id, project: project, client: project.client]
23
+ end
24
+
25
+ def method_missing(method_sym, *arguments, &block)
26
+ if @data.key?(method_sym)
27
+ @data[method_sym]
28
+ else
29
+ super
30
+ end
31
+ end
32
+
33
+ def respond_to?(method_sym, *arguments, &block)
34
+ if @data.key?(method_sym)
35
+ true
36
+ else
37
+ super
38
+ end
39
+ end
40
+
41
+ def title
42
+ @data[:title] || @data[:id].titleize
43
+ end
44
+
45
+ # Validates the fields in the field
46
+ #
47
+ # @return [Array] returns list of the errors represented by hash structures
48
+ def validate
49
+ []
50
+ end
51
+
52
+ def ==(other)
53
+ return false unless other.respond_to?(:data)
54
+ @data == other.data
55
+ end
56
+
57
+ private
58
+
59
+ def validate_presence_of(*fields)
60
+ fields.reduce([]) do |a, e|
61
+ data.key?(e) && !data[e].blank? ? a : a + [e]
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -1,11 +1,11 @@
1
1
  # encoding: UTF-8
2
2
 
3
3
  require_relative 'schema_builder'
4
+ require_relative 'schema_blueprint'
4
5
 
5
6
  module GoodData
6
7
  module Model
7
- class DatasetBlueprint
8
- attr_accessor :data
8
+ class DatasetBlueprint < SchemaBlueprint
9
9
  # Checks if a dataset has an anchor.
10
10
  #
11
11
  # @param dataset [Hash] Dataset blueprint
@@ -19,7 +19,7 @@ module GoodData
19
19
  # @param dataset [Hash] Dataset blueprint
20
20
  # @return [Hash] returns the anchor or nil
21
21
  def self.anchor(dataset)
22
- find_column_by_type(dataset, :anchor, :first)
22
+ find_column_by_type(dataset, :anchor)
23
23
  end
24
24
 
25
25
  # Returns attributes of a dataset
@@ -27,7 +27,15 @@ module GoodData
27
27
  # @param dataset [Hash] Dataset blueprint
28
28
  # @return [Array<Hash>] returns the attribute or an empty array
29
29
  def self.attributes(dataset)
30
- find_column_by_type(dataset, :attribute, :all)
30
+ find_columns_by_type(dataset, :attribute, :all)
31
+ end
32
+
33
+ # Returns attributes and anchor defined on a dataset
34
+ #
35
+ # @param dataset [Hash] Dataset blueprint
36
+ # @return [Array<Hash>] returns the attributes
37
+ def self.attributes_and_anchors(dataset)
38
+ [anchor(dataset)] + attributes(dataset)
31
39
  end
32
40
 
33
41
  # Returns all labels that is referenced by a label
@@ -35,7 +43,7 @@ module GoodData
35
43
  # @param dataset [Hash] Dataset blueprint
36
44
  # @return [Array<Hash>] returns the labels or an empty array
37
45
  def self.attribute_for_label(dataset, label)
38
- find_column_by_type(dataset, [:attribute, :anchor], :all).find { |a| label[:reference] == a[:name] }
46
+ find_columns_by_type(dataset, :attribute, :anchor).find { |a| label[:reference] == a[:id] }
39
47
  end
40
48
 
41
49
  # Returns all the fields of a dataset. This means facts, attributes, references
@@ -43,7 +51,7 @@ module GoodData
43
51
  # @param ds [Hash] Dataset blueprint
44
52
  # @return [Boolean]
45
53
  def self.columns(ds)
46
- ds[:columns] || []
54
+ (ds.to_hash[:columns] || [])
47
55
  end
48
56
  singleton_class.send(:alias_method, :fields, :columns)
49
57
 
@@ -67,7 +75,7 @@ module GoodData
67
75
  # @param dataset [Hash] Dataset blueprint
68
76
  # @return [Array<Hash>] returns the attribute or an empty array
69
77
  def self.date_facts(dataset)
70
- find_column_by_type(dataset, :date_fact, :all)
78
+ find_column_by_type(dataset, :date_fact)
71
79
  end
72
80
 
73
81
  # Returns label that is marked as default for a particular attribtue.
@@ -78,7 +86,7 @@ module GoodData
78
86
  # @return [Array<Hash>] returns the labels or an empty array
79
87
  def self.default_label_for_attribute(dataset, attribute)
80
88
  default_label = labels_for_attribute(dataset, attribute).find { |l| l[:default_label] == true }
81
- default_label || attribute
89
+ default_label
82
90
  end
83
91
 
84
92
  # Returns facts of a dataset
@@ -86,7 +94,7 @@ module GoodData
86
94
  # @param dataset [Hash] Dataset blueprint
87
95
  # @return [Array<Hash>] returns the attribute or an empty array
88
96
  def self.facts(dataset)
89
- find_column_by_type(dataset, [:fact, :date_fact], :all)
97
+ find_columns_by_type(dataset, :fact, :date_fact)
90
98
  end
91
99
 
92
100
  # Finds a specific column given a name
@@ -96,33 +104,32 @@ module GoodData
96
104
  # @param all [Symbol] if :all is passed all mathching objects are returned
97
105
  # Otherwise only the first one is
98
106
  # @return [Array<Hash>] matching fields
99
- def self.find_column_by_name(dataset, name, all = nil)
107
+ def self.find_column_by_id(dataset, name, all = nil)
100
108
  if all == :all
101
- columns(dataset).select { |c| c[:name].to_s == name }
109
+ columns(dataset).select { |c| c[:id].to_s == name }
102
110
  else
103
- columns(dataset).find { |c| c[:name].to_s == name }
111
+ columns(dataset).find { |c| c[:id].to_s == name }
104
112
  end
105
113
  end
106
114
 
107
- # Returns all the fields of a specified type. You can specify more types
108
- # as an array if you need more than one type.
115
+ # Returns first field of a specified type.
109
116
  #
110
- # @param dataset [Hash] Dataset blueprint
111
- # @param type [String | Symbol | Array[Symmbol] | Array[String]] Type or types you would like to get
112
- # @param all [Symbol] if :all is passed
117
+ # @param dataset [Hash | GoodData::Model::ProjectBlueprint] Dataset blueprint
118
+ # @param types [String | Symbol | Array[Symbol] | Array[String]] Type or types you would like to get
113
119
  # as third parameter it return all object otherwise it returns the first one
114
120
  # @return [Array<Hash>] matching fields
115
- def self.find_column_by_type(dataset, type, all = nil)
116
- types = if type.is_a?(Enumerable)
117
- type
118
- else
119
- [type]
120
- end
121
- if all == :all
122
- columns(dataset).select { |c| types.any? { |t| t.to_s == c[:type].to_s } }
123
- else
124
- columns(dataset).find { |c| types.any? { |t| t.to_s == c[:type].to_s } }
125
- end
121
+ def self.find_column_by_type(dataset, *types)
122
+ columns(dataset).find { |c| types.any? { |t| t.to_s == c[:type].to_s } }
123
+ end
124
+
125
+ # Returns all the fields of a specified type. You can specify more types
126
+ # if you need more than one type.
127
+ #
128
+ # @param dataset [Hash | GoodData::Model::ProjectBlueprint] Dataset blueprint
129
+ # @param types [String | Symbol | Array[Symmbol] | Array[String]] Type or types you would like to get
130
+ # @return [Array<Hash>] matching fields
131
+ def self.find_columns_by_type(dataset, *types)
132
+ columns(dataset).select { |c| types.any? { |t| t.to_s == c[:type].to_s } }
126
133
  end
127
134
 
128
135
  # Returns labels facts of a dataset
@@ -130,7 +137,7 @@ module GoodData
130
137
  # @param dataset [Hash] Dataset blueprint
131
138
  # @return [Array<Hash>] returns the label or an empty array
132
139
  def self.labels(dataset)
133
- find_column_by_type(dataset, :label, :all)
140
+ find_columns_by_type(dataset, :label)
134
141
  end
135
142
 
136
143
  # Returns labels for a particular attribute
@@ -139,7 +146,7 @@ module GoodData
139
146
  # @param attribute [Hash] Attribute
140
147
  # @return [Array<Hash>] returns the labels or an empty array
141
148
  def self.labels_for_attribute(dataset, attribute)
142
- labels(dataset).select { |l| l[:reference] == attribute[:name] }
149
+ labels(dataset).select { |l| l[:reference] == attribute[:id] }
143
150
  end
144
151
 
145
152
  # Returns references of a dataset
@@ -147,39 +154,43 @@ module GoodData
147
154
  # @param dataset [Hash] Dataset blueprint
148
155
  # @return [Array<Hash>] returns the references or an empty array
149
156
  def self.references(dataset)
150
- find_column_by_type(dataset, [:reference, :date], :all)
157
+ find_columns_by_type(dataset, :reference, :date)
151
158
  end
152
159
 
153
160
  # Returns anchor of a dataset
154
161
  #
155
162
  # @return [Hash] returns the anchor or nil
156
163
  def anchor
157
- find_column_by_type(:anchor, :first)
164
+ find_column_by_type(:anchor)
158
165
  end
159
166
 
160
167
  # Checks if a dataset has an anchor.
161
168
  #
162
169
  # @return [Boolean] returns true if dataset has an anchor
163
170
  def anchor?
164
- columns.any? { |c| c[:type].to_s == 'anchor' }
171
+ columns.any? { |c| c.type == :anchor }
165
172
  end
166
173
 
167
174
  # Returns attributes of a dataset
168
175
  #
169
176
  # @return [Array<Hash>] returns the attribute or an empty array
170
- def attributes
171
- DatasetBlueprint.attributes(to_hash)
177
+ def attributes(id = :all)
178
+ return id if id.is_a?(AttributeBlueprintField)
179
+ ats = find_columns_by_type(:attribute)
180
+ id == :all ? ats : ats.find { |a| a.id == id }
172
181
  end
173
182
 
183
+ # Returns attributes and anchor defined on a dataset
184
+ #
185
+ # @return [Array<GoodData::Model::DatasetBlueprint>] returns the attributes
174
186
  def attributes_and_anchors
175
- anchor? ? attributes + [anchor] : attributes
187
+ attributes + [anchor]
176
188
  end
177
189
 
178
- # Changes the dataset through a builder. You provide a block with an istance of
179
- # GoodData::Model::SchemaBuilder and you
190
+ # Changes the dataset through a builder. You provide a block and an istance of
191
+ # GoodData::Model::SchemaBuilder is passed in as the only parameter
180
192
  #
181
- # @param dataset [Hash] Dataset blueprint
182
- # @return [Array<Hash>] returns the labels or an empty array
193
+ # @return [GoodData::Model::SchemaBlueprint] returns changed dataset blueprint
183
194
  def change(&block)
184
195
  builder = SchemaBuilder.create_from_data(self)
185
196
  block.call(builder)
@@ -187,11 +198,29 @@ module GoodData
187
198
  self
188
199
  end
189
200
 
190
- # Returns all the fields of a dataset. This means facts, attributes, references
201
+ # Returns all the fields of a dataset. This means anchor, facts, attributes, references
202
+ # This method will cast them to correct types
191
203
  #
192
204
  # @return [Boolean]
193
205
  def columns
194
- DatasetBlueprint.columns(to_hash)
206
+ DatasetBlueprint.columns(to_hash).map do |c|
207
+ case c[:type].to_sym
208
+ when :anchor
209
+ GoodData::Model::AnchorBlueprintField.new(c, self)
210
+ when :attribute
211
+ GoodData::Model::AttributeBlueprintField.new(c, self)
212
+ when :fact
213
+ GoodData::Model::FactBlueprintField.new(c, self)
214
+ when :label
215
+ GoodData::Model::LabelBlueprintField.new(c, self)
216
+ when :reference
217
+ GoodData::Model::ReferenceBlueprintField.new(c, self)
218
+ when :date
219
+ GoodData::Model::ReferenceBlueprintField.new(c, self)
220
+ else
221
+ GoodData::Model::BlueprintField.new(c, self)
222
+ end
223
+ end
195
224
  end
196
225
  alias_method :fields, :columns
197
226
 
@@ -200,87 +229,99 @@ module GoodData
200
229
  #
201
230
  # @return [Boolean]
202
231
  def count(project)
203
- id = if anchor?
204
- GoodData::Model.identifier_for(to_hash, anchor)
205
- else
206
- GoodData::Model.identifier_for(to_hash, type: :anchor_no_label)
207
- end
208
- attribute = project.attributes(id)
209
- attribute.create_metric.execute
232
+ anchor.in_project(project).create_metric.execute
210
233
  end
211
234
 
212
235
  # Returns date facts of a dataset
213
236
  #
214
237
  # @return [Array<Hash>] returns the attribute or an empty array
215
238
  def date_facts
216
- DatasetBlueprint.date_facts(to_hash)
239
+ find_columns_by_type(:date_fact)
217
240
  end
218
241
 
219
242
  # Duplicates the DatasetBlueprint. It is done as a deep duplicate
220
243
  #
221
244
  # @return [GoodData::Model::DatasetBlueprint] matching fields
222
245
  def dup
223
- DatasetBlueprint.new(data.deep_dup)
246
+ DatasetBlueprint.new(data.deep_dup, project_blueprint)
224
247
  end
225
248
 
226
- # Compares two blueprints. This is done by comapring the hash represenatation.
227
- # It has to be exacty identical including the order of the columns
249
+ # Returns facts of a dataset
228
250
  #
229
- # @param name [GoodData::Model::DatasetBlueprint] Name of a field
230
- # @return [Boolean] matching fields
231
- def eql?(other)
232
- to_hash == other.to_hash
251
+ # @return [Array<Hash>] returns the attribute or an empty array
252
+ def facts(id = :all)
253
+ return id if id.is_a?(FactBlueprintField)
254
+ fs = find_columns_by_type(:fact)
255
+ id == :all ? fs : fs.find { |a| a.id == id }
233
256
  end
234
257
 
235
- # Returns facts of a dataset
258
+ # Finds a specific column given a col
236
259
  #
237
- # @return [Array<Hash>] returns the attribute or an empty array
238
- def facts
239
- DatasetBlueprint.facts(to_hash)
260
+ # @param col [GoodData::Model::BlueprintField | Hash] Field
261
+ # @return [GoodData::Model::BlueprintField] matching fields
262
+ def find_column(col)
263
+ columns.find { |c| c == col }
240
264
  end
241
265
 
242
- # Finds a specific column given a name
266
+ # Finds a specific column given an id
243
267
  #
244
- # @param name [String] Name of a field
268
+ # @param id [String] Id of a field
245
269
  # @param all [Symbol] if :all is passed all mathching objects are returned
246
270
  # Otherwise only the first one is
247
271
  # @return [Array<Hash>] matching fields
248
- def find_column_by_name(type, all = :all)
249
- DatasetBlueprint.find_column_by_name(to_hash, type, all)
272
+ def find_column_by_id(id)
273
+ id = id.respond_to?(:id) ? id.id : id
274
+ columns.find { |c| c.id == id }
250
275
  end
251
276
 
252
- # Returns all the fields of a specified type. You can specify more types
253
- # as an array if you need more than one type.
277
+ # Returns first field of a specified type.
254
278
  #
255
279
  # @param type [String | Symbol | Array[Symmbol] | Array[String]] Type or types you would like to get
256
- # @param all [Symbol] if :all is passed
257
- # as third parameter it return all object otherwise it returns the first one
258
- # @return [Array<Hash>] matching fields
259
- def find_column_by_type(type, all = nil)
260
- DatasetBlueprint.find_column_by_type(to_hash, type, all)
280
+ # @return [GoodData::Model::BlueprintField] returns matching field
281
+ def find_column_by_type(*types)
282
+ columns.find { |c| types.any? { |t| t.downcase.to_sym == c.type } }
261
283
  end
262
284
 
263
- # Returns identifier for dataset
285
+ # Returns all the fields of a specified type. You can specify more types
286
+ # as an array if you need more than one type.
264
287
  #
265
- # @return [String] identifier
266
- def identifier
267
- GoodData::Model.identifier_for(to_hash)
288
+ # @param type [String | Symbol | Array[Symmbol] | Array[String]] Type or types you would like to get
289
+ # as third parameter it return all object otherwise it returns the first one
290
+ # @return [Array<GoodData::Model::BlueprintField>] matching fields
291
+ def find_columns_by_type(*types)
292
+ columns.select { |c| types.any? { |t| t.downcase.to_sym == c.type } }
268
293
  end
269
294
 
270
295
  # Creates a DatasetBlueprint
271
296
  #
272
297
  # @param dataset [Hash] Dataset blueprint
273
298
  # @return [DatasetBlueprint] returns the labels or an empty array
274
- def initialize(init_data)
275
- @data = init_data
299
+ def initialize(init_data, blueprint)
300
+ super
301
+ @data[:type] = @data.key?('type') ? @data['type'].to_sym : @data[:type]
302
+ @data[:columns].each do |c|
303
+ c[:type] = c[:type].to_sym
304
+ end
276
305
  end
277
306
 
278
307
  # Returns labels facts of a dataset
279
308
  #
280
309
  # @param dataset [Hash] Dataset blueprint
281
310
  # @return [Array<Hash>] returns the label or an empty array
282
- def labels
283
- DatasetBlueprint.labels(to_hash)
311
+ def labels(id = :all)
312
+ return id if id.is_a?(LabelBlueprintField)
313
+ labs = find_columns_by_type(:label)
314
+ id == :all ? labs : labs.find { |l| l.id == id }
315
+ end
316
+
317
+ def attribute_for_label(label)
318
+ l = labels(label)
319
+ attributes_and_anchors.find { |a| a.id == l.reference }
320
+ end
321
+
322
+ def labels_for_attribute(attribute)
323
+ a = attributes(attribute)
324
+ labels.select { |l| l.reference == a.id }
284
325
  end
285
326
 
286
327
  # Merges two schemas together. This method changes the blueprint
@@ -295,18 +336,26 @@ module GoodData
295
336
  self
296
337
  end
297
338
 
298
- # Returns name of the dataset
299
- #
300
- # @return [String]
301
- def name
302
- data[:name]
303
- end
304
-
305
339
  # Returns references of a dataset
306
340
  #
307
341
  # @return [Array<Hash>] returns the references or an empty array
308
342
  def references
309
- DatasetBlueprint.references(to_hash)
343
+ find_columns_by_type(:reference, :date)
344
+ end
345
+
346
+ # Removes column from from the blueprint
347
+ #
348
+ # @param id [String] Id of the column to be removed
349
+ # @return [GoodData::Model::ProjectBlueprint] Returns changed blueprint
350
+ def remove_column!(id)
351
+ @project_blueprint.remove_column!(self, id)
352
+ end
353
+
354
+ # Removes all the labels from the anchor. This is a typical operation that people want to perform
355
+ #
356
+ # @return [GoodData::Model::ProjectBlueprint] Returns changed blueprint
357
+ def strip_anchor!
358
+ @project_blueprint.strip_anchor!(self)
310
359
  end
311
360
 
312
361
  # Method for suggest a couple of metrics that might get you started
@@ -323,43 +372,62 @@ module GoodData
323
372
  end
324
373
  end
325
374
 
326
- # Returns title of the dataset. If it is not set up. It is generated for you
327
- # based on the name which is titleized
375
+ def to_blueprint
376
+ GoodData::Model::ProjectBlueprint.new(datasets: [to_hash])
377
+ end
378
+
379
+ # Validate the blueprint return array of errors that are found.
328
380
  #
329
- # @return [String]
330
- def title
331
- data[:title] || name.titleize
381
+ # @return [Array] array of errors
382
+ def validate
383
+ errors = []
384
+ errors.concat(validate_more_anchors)
385
+ errors.concat(validate_some_anchors)
386
+ errors.concat(validate_label_references)
387
+ errors.concat(validate_gd_data_type_errors)
388
+ errors.concat(fields.flat_map(&:validate))
389
+ errors.concat(validate_attribute_has_one_label)
390
+ errors
332
391
  end
333
392
 
334
- # Returns hash representation which is much better suited for processing
393
+ # Validate if the dataset has more than zero anchors defined.
335
394
  #
336
- # @return [Hash]
337
- def to_hash
338
- data
395
+ # @return [Array] array of errors
396
+ def validate_some_anchors
397
+ find_columns_by_type(:anchor).count == 0 ? [{ type: :no_anchor, dataset: id }] : []
339
398
  end
340
399
 
341
- # Validate the blueprint return array of errors that are found.
400
+ # Validate if the dataset does not have more than one anchor defined.
342
401
  #
343
402
  # @return [Array] array of errors
344
- def validate
345
- more_than_one_anchor = find_column_by_type(:anchor, :all).count > 1 ? [{ :anchor => 2 }] : []
346
- validate_label_references.concat(more_than_one_anchor)
403
+ def validate_more_anchors
404
+ find_columns_by_type(:anchor).count > 1 ? [{ type: :more_than_on_anchor, dataset: id }] : []
347
405
  end
348
406
 
349
- # Validate the blueprint and return true if model is valid. False otherwise.
407
+ # Validate if the attribute does have at least one label
350
408
  #
351
- # @return [Boolean] is model valid?
352
- def valid?
353
- validate.empty?
409
+ # @return [Array] array of errors
410
+ def validate_attribute_has_one_label
411
+ find_columns_by_type(:attribute)
412
+ .select { |a| a.labels.empty? }
413
+ .map { |e| { type: :attribute_without_label, attribute: e.id } }
354
414
  end
355
415
 
356
416
  # Validate the that any labels are pointing to the existing attribute. If not returns the list of errors. Currently just violating labels.
357
417
  #
358
418
  # @return [Array] array of errors
359
419
  def validate_label_references
360
- labels.select do |label|
361
- find_column_by_name(label[:reference]).empty?
362
- end
420
+ labels.select { |r| r.attribute.nil? }
421
+ .map { |er_ref| { type: :wrong_label_reference, label: er_ref.id, wrong_reference: er_ref.data[:reference] } }
422
+ end
423
+
424
+ # Validate the the used gd_data_types are one of the allowed types. The data types are checked on lables and facts.
425
+ #
426
+ # @return [Array] array of errors
427
+ def validate_gd_data_type_errors
428
+ (labels + facts)
429
+ .select { |x| x.gd_data_type && !GoodData::Model.check_gd_data_type(x.gd_data_type) }
430
+ .map { |e| { :error => :invalid_gd_data_type_specified, :column => e.id } }
363
431
  end
364
432
 
365
433
  # Helper methods to decide wheather the dataset is considered wide.
@@ -370,15 +438,6 @@ module GoodData
370
438
  def wide?
371
439
  fields.count > 32
372
440
  end
373
-
374
- # Compares two blueprints. This is done by comapring the hash represenatation.
375
- # It has to be exacty identical including the order of the columns
376
- #
377
- # @param name [GoodData::Model::DatasetBlueprint] Name of a field
378
- # @return [Boolean] matching fields
379
- def ==(other)
380
- to_hash == other.to_hash
381
- end
382
441
  end
383
442
  end
384
443
  end