gooddata 0.6.18 → 0.6.19

Sign up to get free protection for your applications and to get access to all the features.
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