gooddata 0.6.3 → 0.6.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +3 -1
- data/CHANGELOG.markdown +6 -0
- data/README.md +1 -0
- data/gooddata.gemspec +2 -1
- data/lib/gooddata.rb +4 -1
- data/lib/gooddata/bricks/base_downloader.rb +33 -19
- data/lib/gooddata/bricks/middleware/bulk_salesforce_middleware.rb +49 -25
- data/lib/gooddata/bricks/middleware/restforce_middleware.rb +36 -33
- data/lib/gooddata/cli/commands/project_cmd.rb +6 -4
- data/lib/gooddata/client.rb +1 -1
- data/lib/gooddata/commands/api.rb +1 -1
- data/lib/gooddata/commands/auth.rb +1 -1
- data/lib/gooddata/connection.rb +13 -10
- data/lib/gooddata/core/connection.rb +1 -1
- data/lib/gooddata/core/user.rb +11 -3
- data/lib/gooddata/exceptions/validation_error.rb +12 -0
- data/lib/gooddata/extensions/extensions.rb +6 -0
- data/lib/gooddata/goodzilla/goodzilla.rb +2 -2
- data/lib/gooddata/helpers/csv_helper.rb +57 -0
- data/lib/gooddata/{helpers.rb → helpers/global_helpers.rb} +0 -0
- data/lib/gooddata/helpers/helpers.rb +6 -0
- data/lib/gooddata/models/domain.rb +134 -24
- data/lib/gooddata/models/membership.rb +402 -0
- data/lib/gooddata/models/metadata.rb +64 -7
- data/lib/gooddata/models/metadata/attribute.rb +27 -12
- data/lib/gooddata/models/metadata/column.rb +1 -1
- data/lib/gooddata/models/metadata/dashboard.rb +7 -6
- data/lib/gooddata/models/metadata/display_form.rb +17 -2
- data/lib/gooddata/models/metadata/fact.rb +13 -7
- data/lib/gooddata/models/metadata/metric.rb +9 -9
- data/lib/gooddata/models/metadata/report.rb +7 -8
- data/lib/gooddata/models/metadata/report_definition.rb +10 -11
- data/lib/gooddata/models/metadata/schema.rb +1 -1
- data/lib/gooddata/models/model.rb +1 -1
- data/lib/gooddata/models/process.rb +44 -25
- data/lib/gooddata/models/profile.rb +365 -13
- data/lib/gooddata/models/project.rb +245 -35
- data/lib/gooddata/models/project_blueprint.rb +42 -18
- data/lib/gooddata/models/project_creator.rb +4 -1
- data/lib/gooddata/models/project_role.rb +7 -7
- data/lib/gooddata/models/schedule.rb +17 -1
- data/lib/gooddata/models/schema_blueprint.rb +19 -2
- data/lib/gooddata/version.rb +1 -1
- data/out.txt +0 -0
- data/spec/data/users.csv +12 -0
- data/spec/helpers/connection_helper.rb +1 -0
- data/spec/helpers/csv_helper.rb +12 -0
- data/spec/helpers/project_helper.rb +1 -1
- data/spec/integration/full_project_spec.rb +136 -3
- data/spec/spec_helper.rb +9 -0
- data/spec/unit/commands/command_user_spec.rb +1 -1
- data/spec/unit/extensions/hash_spec.rb +19 -0
- data/spec/unit/godzilla/goodzilla_spec.rb +15 -0
- data/spec/unit/helpers/csv_helper_spec.rb +18 -0
- data/spec/unit/models/domain_spec.rb +47 -4
- data/spec/unit/models/md_object_spec.rb +8 -0
- data/spec/unit/models/membership_spec.rb +128 -0
- data/spec/unit/models/metadata_spec.rb +38 -0
- data/spec/unit/models/profile_spec.rb +212 -0
- data/spec/unit/models/project_blueprint_spec.rb +35 -8
- data/spec/unit/models/project_role_spec.rb +6 -6
- data/spec/unit/models/project_spec.rb +226 -13
- data/spec/unit/models/schedule_spec.rb +58 -0
- data/tmp/.gitkeepme +0 -0
- metadata +36 -11
- data/lib/gooddata/models/account_settings.rb +0 -124
- data/lib/gooddata/models/user.rb +0 -165
- data/spec/unit/models/account_settings_spec.rb +0 -28
- data/spec/unit/models/user_spec.rb +0 -16
@@ -38,7 +38,10 @@ module GoodData
|
|
38
38
|
# @return [MdObject] if id is a String or number single object is returned
|
39
39
|
# @return [Array] if :all was provided as an id, list of objects should be returned. Note that this is implemented only in the subclasses. MdObject does not support this since API has no means to return list of all types of objects
|
40
40
|
def [](id, options = {})
|
41
|
-
fail "
|
41
|
+
fail "You have to provide an \"id\" to be searched for." unless id
|
42
|
+
fail(NoProjectError, 'Connect to a project before searching for an object') unless GoodData.project
|
43
|
+
return all(options) if id == :all
|
44
|
+
return id if id.is_a?(MdObject)
|
42
45
|
uri = if id.is_a?(Integer) || id =~ /^\d+$/
|
43
46
|
"#{GoodData.project.md[MD_OBJ_CTG]}/#{id}"
|
44
47
|
elsif id !~ /\//
|
@@ -51,8 +54,13 @@ module GoodData
|
|
51
54
|
new(GoodData.get uri) unless uri.nil?
|
52
55
|
end
|
53
56
|
|
57
|
+
# Method intended to get all objects of that type in a specified project
|
58
|
+
#
|
59
|
+
# @param options [Hash] the options hash
|
60
|
+
# @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
|
61
|
+
# @return [Array<GoodData::MdObject> | Array<Hash>] Return the appropriate metadata objects or their representation
|
54
62
|
def all(options = {})
|
55
|
-
|
63
|
+
fail NotImplementedError, 'Method should be implemented in subclass. Currently there is no way hoe to get all metadata objects on API.'
|
56
64
|
end
|
57
65
|
|
58
66
|
def find_by_tag(tag)
|
@@ -92,18 +100,42 @@ module GoodData
|
|
92
100
|
if response['identifiers'].empty?
|
93
101
|
nil
|
94
102
|
else
|
95
|
-
|
96
|
-
|
103
|
+
identifiers = response['identifiers']
|
104
|
+
ids_lookup = identifiers.reduce({}) do |a, e|
|
105
|
+
a[e['identifier']] = e['uri']
|
106
|
+
a
|
107
|
+
end
|
108
|
+
uris = ids.map { |x| ids_lookup[x] }
|
109
|
+
uris.count == 1 ? uris.first : uris
|
97
110
|
end
|
98
111
|
end
|
99
112
|
|
100
113
|
alias_method :id_to_uri, :identifier_to_uri
|
101
114
|
|
102
115
|
alias_method :get_by_id, :[]
|
116
|
+
|
117
|
+
private
|
118
|
+
|
119
|
+
# Method intended to be called by individual classes in their all
|
120
|
+
# implementations. It abstracts the way interacting with query resources.
|
121
|
+
# It either returns the array of hashes from query. If asked it also
|
122
|
+
# goes and brings the full objects. Due to performance reasons
|
123
|
+
# :full => false is the default. This will most likely change
|
124
|
+
#
|
125
|
+
# @param query_obj_type [String] string used in URI to distinguish different query resources for different objects
|
126
|
+
# @param klass [Class] A class used for instantiating the returned data
|
127
|
+
# @param options [Hash] the options hash
|
128
|
+
# @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
|
129
|
+
# @return [Array<GoodData::MdObject> | Array<Hash>] Return the appropriate metadata objects or their representation
|
130
|
+
def query(query_obj_type, klass, options = {})
|
131
|
+
fail(NoProjectError, 'Connect to a project before searching for an object') unless GoodData.project
|
132
|
+
query_result = GoodData.get(GoodData.project.md['query'] + "/#{query_obj_type}/")['query']['entries']
|
133
|
+
options[:full] ? query_result.map { |item| klass[item['link']] } : query_result
|
134
|
+
end
|
103
135
|
end
|
104
136
|
|
105
137
|
metadata_property_reader :uri, :identifier, :title, :summary, :tags, :deprecated, :category
|
106
|
-
metadata_property_writer :tags, :summary, :title
|
138
|
+
metadata_property_writer :tags, :summary, :title, :identifier
|
107
139
|
|
108
140
|
def root_key
|
109
141
|
raw_data.keys.first
|
@@ -237,8 +269,21 @@ module GoodData
|
|
237
269
|
self
|
238
270
|
end
|
239
271
|
|
272
|
+
# Saves an object with a different name
|
273
|
+
#
|
274
|
+
# @param new_title [String] New title. If not provided one is provided
|
275
|
+
# @return [GoodData::MdObject] MdObject that has been saved as
|
276
|
+
def save_as(new_title = "Clone of #{title}")
|
277
|
+
dupped = Marshal.load(Marshal.dump(raw_data))
|
278
|
+
dupped[root_key]['meta'].delete('uri')
|
279
|
+
dupped[root_key]['meta'].delete('identifier')
|
280
|
+
dupped[root_key]['meta']['title'] = new_title
|
281
|
+
x = self.class.new(dupped)
|
282
|
+
x.save
|
283
|
+
end
|
284
|
+
|
240
285
|
def ==(other)
|
241
|
-
other.uri == uri && other.respond_to?(:to_hash) && other.to_hash == to_hash
|
286
|
+
other.respond_to?(:uri) && other.uri == uri && other.respond_to?(:to_hash) && other.to_hash == to_hash
|
242
287
|
end
|
243
288
|
|
244
289
|
def validate
|
@@ -249,19 +294,31 @@ module GoodData
|
|
249
294
|
false
|
250
295
|
end
|
251
296
|
|
252
|
-
#
|
297
|
+
# Returns true if the object is a fact false otherwise
|
298
|
+
# @return [Boolean]
|
253
299
|
def fact?
|
254
300
|
false
|
255
301
|
end
|
256
302
|
|
303
|
+
# Returns true if the object is an attribute false otherwise
|
304
|
+
# @return [Boolean]
|
257
305
|
def attribute?
|
258
306
|
false
|
259
307
|
end
|
260
308
|
|
309
|
+
# Returns true if the object is a metric false otherwise
|
310
|
+
# @return [Boolean]
|
261
311
|
def metric?
|
262
312
|
false
|
263
313
|
end
|
264
314
|
|
315
|
+
# Returns true if the object is a label false otherwise
|
316
|
+
# @return [Boolean]
|
317
|
+
def label?
|
318
|
+
false
|
319
|
+
end
|
320
|
+
alias_method :display_form?, :label?
|
321
|
+
|
265
322
|
private
|
266
323
|
|
267
324
|
def dependency(uri, key = nil)
|
@@ -9,44 +9,56 @@ module GoodData
|
|
9
9
|
ATTRIBUTE_BASE_AGGREGATIONS = [:count]
|
10
10
|
|
11
11
|
class << self
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
12
|
+
# Method intended to get all objects of that type in a specified project
|
13
|
+
#
|
14
|
+
# @param options [Hash] the options hash
|
15
|
+
# @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
|
16
|
+
# @return [Array<GoodData::MdObject> | Array<Hash>] Return the appropriate metadata objects or their representation
|
17
|
+
def all(options = {})
|
18
|
+
query('attributes', Attribute, options)
|
19
19
|
end
|
20
20
|
|
21
|
+
# Finds the value of an atribute and gives you the textual form for the label that is acquired by calling primary_label method
|
22
|
+
#
|
23
|
+
# @param uri [String] Uri of the element. in the form of /gdc/md/PID/obj/OBJ_ID/elements?id=21
|
24
|
+
# @return [String] Textual representation of a particular attribute element
|
21
25
|
def find_element_value(uri)
|
22
26
|
matches = uri.match(/(.*)\/elements\?id=(\d+)$/)
|
23
|
-
Attribute[matches[1]].primary_label.find_element_value(
|
27
|
+
Attribute[matches[1]].primary_label.find_element_value(uri)
|
24
28
|
end
|
25
29
|
end
|
26
30
|
|
31
|
+
# Returns the labels of an attribute
|
32
|
+
# @return [Array<GoodData::Label>]
|
27
33
|
def display_forms
|
28
|
-
content['displayForms'].map { |df| GoodData::
|
34
|
+
content['displayForms'].map { |df| GoodData::Label[df['meta']['uri']] }
|
29
35
|
end
|
30
36
|
alias_method :labels, :display_forms
|
31
37
|
|
32
38
|
# Returns the first display form which is the primary one
|
33
|
-
# @return [GoodData::
|
39
|
+
# @return [GoodData::Label] Primary label
|
34
40
|
def primary_display_form
|
35
41
|
labels.first
|
36
42
|
end
|
37
43
|
alias_method :primary_label, :primary_display_form
|
38
44
|
|
45
|
+
# Returns true if the object is an attribute false otherwise
|
46
|
+
# @return [Boolean]
|
39
47
|
def attribute?
|
40
48
|
true
|
41
49
|
end
|
42
50
|
|
51
|
+
# Creates the basic count metric with the attribute used. If you need to compute the attribute on a different dataset you can specify that in params. The metric created is not saved.
|
52
|
+
# @param [Hash] options the options to pass to the value list
|
53
|
+
# @option options [Symbol] :type type of aggregation function.
|
54
|
+
# @option options [Symbol] :attribute Use this attribute if you need to express different dataset for performing the computation on. It basically serves for creating metrics like SELECT COUNT(User, Opportunity).
|
55
|
+
# @return [GoodData::Metric]
|
43
56
|
def create_metric(options = {})
|
44
57
|
an_attribute = options[:attribute]
|
45
58
|
a_type = options[:type] || :count
|
46
59
|
fail "Suggested aggreagtion function (#{a_type}) does not exist for base metric created out of attribute. You can use only one of #{ATTRIBUTE_BASE_AGGREGATIONS.map { |x| ":" + x.to_s }.join(',')}" unless ATTRIBUTE_BASE_AGGREGATIONS.include?(a_type)
|
47
60
|
a_title = options[:title] || "#{a_type} of #{title}"
|
48
61
|
if an_attribute
|
49
|
-
an_attribute = Attribute[an_attribute] if an_attribute.is_a?(String)
|
50
62
|
Metric.xcreate(:expression => "SELECT #{a_type.to_s.upcase}(![#{identifier}], ![#{an_attribute.identifier}])", :title => a_title)
|
51
63
|
else
|
52
64
|
Metric.xcreate(:expression => "SELECT #{a_type.to_s.upcase}(![#{identifier}])", :title => a_title)
|
@@ -57,7 +69,7 @@ module GoodData
|
|
57
69
|
# @param [Object] element_id Element identifier either Number or a uri as a String
|
58
70
|
# @return [Array] list of values for certain element. Returned in the same order as is the order of labels
|
59
71
|
def values_for(element_id)
|
60
|
-
element_id = element_id.is_a?(String) ? element_id.match(/\?id=(\d)/)[1] : element_id
|
72
|
+
# element_id = element_id.is_a?(String) ? element_id.match(/\?id=(\d)/)[1] : element_id
|
61
73
|
labels.map do |label|
|
62
74
|
label.find_element_value(element_id)
|
63
75
|
end
|
@@ -74,6 +86,9 @@ module GoodData
|
|
74
86
|
results.first.zip(*results[1..-1])
|
75
87
|
end
|
76
88
|
|
89
|
+
# Allows to search in attribute labels by name. It uses the string as a basis for regexp and tries to match either a title or an identifier. Returns first match.
|
90
|
+
# @param name [String] name used as a basis for regular expression
|
91
|
+
# @return [GoodData::Label]
|
77
92
|
def label_by_name(name)
|
78
93
|
labels.find { |label| label.title.downcase =~ /#{name}/ || label.identifier.downcase =~ /#{name}/ }
|
79
94
|
end
|
@@ -12,12 +12,13 @@ module GoodData
|
|
12
12
|
root_key :projectDashboard
|
13
13
|
|
14
14
|
class << self
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
15
|
+
# Method intended to get all objects of that type in a specified project
|
16
|
+
#
|
17
|
+
# @param options [Hash] the options hash
|
18
|
+
# @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
|
19
|
+
# @return [Array<GoodData::MdObject> | Array<Hash>] Return the appropriate metadata objects or their representation
|
20
|
+
def all(options = {})
|
21
|
+
query('projectdashboards', Dashboard, options)
|
21
22
|
end
|
22
23
|
|
23
24
|
def create_report_tab(tab)
|
@@ -4,14 +4,14 @@ require_relative '../metadata'
|
|
4
4
|
require_relative 'metadata'
|
5
5
|
|
6
6
|
module GoodData
|
7
|
-
class
|
7
|
+
class Label < GoodData::MdObject
|
8
8
|
root_key :attributeDisplayForm
|
9
9
|
|
10
10
|
# Finds an attribute element URI for given value. This URI can be used by find_element_value to find the original value again
|
11
11
|
# @param [String] value value of an label you are looking for
|
12
12
|
# @return [String]
|
13
13
|
def find_value_uri(value)
|
14
|
-
value = CGI.
|
14
|
+
value = CGI.escape(value)
|
15
15
|
results = GoodData.post("#{uri}/validElements?limit=30&offset=0&order=asc&filter=#{value}", {})
|
16
16
|
items = results['validElements']['items']
|
17
17
|
if items.empty?
|
@@ -57,5 +57,20 @@ module GoodData
|
|
57
57
|
def attribute
|
58
58
|
GoodData::Attribute[content['formOf']]
|
59
59
|
end
|
60
|
+
|
61
|
+
# Returns true if the object is an attribute false otherwise
|
62
|
+
# @return [Boolean]
|
63
|
+
def label?
|
64
|
+
true
|
65
|
+
end
|
66
|
+
alias_method :display_form?, :label?
|
67
|
+
|
68
|
+
# Gives an attribute url of current label. Useful for mass actions when it does not introduce HTTP call.
|
69
|
+
# @return [GoodData::Attibute]
|
70
|
+
def attribute_uri
|
71
|
+
content['formOf']
|
72
|
+
end
|
60
73
|
end
|
61
74
|
end
|
75
|
+
|
76
|
+
GoodData::DisplayForm = GoodData::Label
|
@@ -12,20 +12,26 @@ module GoodData
|
|
12
12
|
FACT_BASE_AGGREGATIONS = [:sum, :min, :max, :avg, :median]
|
13
13
|
|
14
14
|
class << self
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
15
|
+
# Method intended to get all objects of that type in a specified project
|
16
|
+
#
|
17
|
+
# @param options [Hash] the options hash
|
18
|
+
# @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
|
19
|
+
# @return [Array<GoodData::MdObject> | Array<Hash>] Return the appropriate metadata objects or their representation
|
20
|
+
def all(options = {})
|
21
|
+
query('facts', Fact, options)
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
25
|
+
# Returns true if the object is a fact false otherwise
|
26
|
+
# @return [Boolean]
|
25
27
|
def fact?
|
26
28
|
true
|
27
29
|
end
|
28
30
|
|
31
|
+
# Creates the basic count metric with the fact used. The metric created is not saved.
|
32
|
+
# @param [Hash] options the options to pass to the value list
|
33
|
+
# @option options [Symbol] :type type of aggregation function. Default is :sum
|
34
|
+
# @return [GoodData::Metric]
|
29
35
|
def create_metric(options = {})
|
30
36
|
a_type = options[:type] || :sum
|
31
37
|
fail "Suggested aggreagtion function (#{a_type}) does not exist for base metric created out of fact. You can use only one of #{FACT_BASE_AGGREGATIONS.map { |x| ":" + x.to_s }.join(',')}" unless FACT_BASE_AGGREGATIONS.include?(a_type)
|
@@ -12,13 +12,13 @@ module GoodData
|
|
12
12
|
PARSE_MAQL_OBJECT_REGEXP = /\[([^\]]+)\]/
|
13
13
|
|
14
14
|
class << self
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
15
|
+
# Method intended to get all objects of that type in a specified project
|
16
|
+
#
|
17
|
+
# @param options [Hash] the options hash
|
18
|
+
# @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
|
19
|
+
# @return [Array<GoodData::MdObject> | Array<Hash>] Return the appropriate metadata objects or their representation
|
20
|
+
def all(options = {})
|
21
|
+
query('metrics', Metric, options)
|
22
22
|
end
|
23
23
|
|
24
24
|
def xcreate(options)
|
@@ -137,7 +137,7 @@ module GoodData
|
|
137
137
|
end
|
138
138
|
|
139
139
|
# Checks that the expression contains certain element of an attribute. The value is looked up through given label.
|
140
|
-
# @param [GoodData::
|
140
|
+
# @param [GoodData::Label] label Label though which the value is looked up
|
141
141
|
# @param [String] value Value that will be looked up through the label.
|
142
142
|
# @return [Boolean]
|
143
143
|
def contain_value?(label, value)
|
@@ -157,7 +157,7 @@ module GoodData
|
|
157
157
|
end
|
158
158
|
|
159
159
|
# Method used for replacing attribute element values. Looks up certain value of a label in the MAQL expression and exchanges it for a different value of the same label.
|
160
|
-
# @param [GoodData::
|
160
|
+
# @param [GoodData::Label] label Label through which the value and for_value are resolved
|
161
161
|
# @param [String] value value that is going to be replaced
|
162
162
|
# @param [String] for_value value that is going to be the new one
|
163
163
|
# @return [GoodData::Metric]
|
@@ -8,14 +8,13 @@ module GoodData
|
|
8
8
|
root_key :report
|
9
9
|
|
10
10
|
class << self
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
end
|
11
|
+
# Method intended to get all objects of that type in a specified project
|
12
|
+
#
|
13
|
+
# @param options [Hash] the options hash
|
14
|
+
# @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
|
15
|
+
# @return [Array<GoodData::MdObject> | Array<Hash>] Return the appropriate metadata objects or their representation
|
16
|
+
def all(options = {})
|
17
|
+
query('reports', Report, options)
|
19
18
|
end
|
20
19
|
|
21
20
|
def create(options = {})
|
@@ -11,14 +11,13 @@ module GoodData
|
|
11
11
|
root_key :reportDefinition
|
12
12
|
|
13
13
|
class << self
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
end
|
14
|
+
# Method intended to get all objects of that type in a specified project
|
15
|
+
#
|
16
|
+
# @param options [Hash] the options hash
|
17
|
+
# @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
|
18
|
+
# @return [Array<GoodData::MdObject> | Array<Hash>] Return the appropriate metadata objects or their representation
|
19
|
+
def all(options = {})
|
20
|
+
query('reportdefinition', ReportDefinition, options)
|
22
21
|
end
|
23
22
|
|
24
23
|
def create_metrics_part(left, top)
|
@@ -72,7 +71,7 @@ module GoodData
|
|
72
71
|
when 'attribute'
|
73
72
|
GoodData::Attribute.new(x.raw_data).display_forms.first
|
74
73
|
when 'attributeDisplayForm'
|
75
|
-
GoodData::
|
74
|
+
GoodData::Label.new(x.raw_data)
|
76
75
|
when 'metric'
|
77
76
|
GoodData::Metric.new(x.raw_data)
|
78
77
|
end
|
@@ -91,7 +90,7 @@ module GoodData
|
|
91
90
|
when 'attribute'
|
92
91
|
GoodData::Attribute.get_by_id(item[:id]).display_forms.first
|
93
92
|
when 'label'
|
94
|
-
GoodData::
|
93
|
+
GoodData::Label.get_by_id(item[:id])
|
95
94
|
end
|
96
95
|
elsif item.is_a?(Hash) && (item.keys.include?(:identifier))
|
97
96
|
case item[:type].to_s
|
@@ -101,7 +100,7 @@ module GoodData
|
|
101
100
|
result = GoodData::Attribute.get_by_id(item[:identifier])
|
102
101
|
result.display_forms.first
|
103
102
|
when 'label'
|
104
|
-
GoodData::
|
103
|
+
GoodData::Label.get_by_id(item[:identifier])
|
105
104
|
end
|
106
105
|
else
|
107
106
|
item
|
@@ -91,7 +91,7 @@ module GoodData
|
|
91
91
|
def merge_dataset_columns(a_schema_blueprint, b_schema_blueprint)
|
92
92
|
a_schema_blueprint = a_schema_blueprint.to_hash
|
93
93
|
b_schema_blueprint = b_schema_blueprint.to_hash
|
94
|
-
d =
|
94
|
+
d = a_schema_blueprint.deep_dup
|
95
95
|
d[:columns] = d[:columns] + b_schema_blueprint[:columns]
|
96
96
|
d[:columns].uniq!
|
97
97
|
columns_that_failed_to_merge = d[:columns].group_by { |x| x[:name] }.map { |k, v| [k, v.count] }.select { |x| x[1] > 1 }
|
@@ -42,41 +42,54 @@ module GoodData
|
|
42
42
|
end
|
43
43
|
end
|
44
44
|
|
45
|
-
def upload_package(
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
45
|
+
def upload_package(path, files_to_exclude)
|
46
|
+
if !path.directory?
|
47
|
+
GoodData.upload_to_user_webdav(path)
|
48
|
+
path
|
49
|
+
else
|
50
|
+
Tempfile.open('deploy-graph-archive') do |temp|
|
51
|
+
Zip::OutputStream.open(temp.path) do |zio|
|
52
|
+
FileUtils.cd(path) do
|
53
|
+
|
54
|
+
files_to_pack = Dir.glob('./**/*').reject { |f| files_to_exclude.include?(Pathname(path) + f) }
|
55
|
+
files_to_pack.each do |item|
|
56
|
+
# puts "including #{item}" if verbose
|
57
|
+
unless File.directory?(item)
|
58
|
+
zio.put_next_entry(item)
|
59
|
+
zio.print IO.read(item)
|
60
|
+
end
|
56
61
|
end
|
57
62
|
end
|
58
63
|
end
|
64
|
+
GoodData.upload_to_user_webdav(temp.path)
|
65
|
+
temp.path
|
59
66
|
end
|
60
|
-
GoodData.upload_to_user_webdav(temp.path)
|
61
|
-
temp
|
62
67
|
end
|
63
68
|
end
|
64
69
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
70
|
+
# Deploy a new process or redeploy existing one.
|
71
|
+
#
|
72
|
+
# @param path [String] Path to ZIP archive or to a directory containing files that should be ZIPed
|
73
|
+
# @option options [String] :files_to_exclude
|
74
|
+
# @option options [String] :process_id ('nobody') From address
|
75
|
+
# @option options [String] :type ('GRAPH') Type of process - GRAPH or RUBY
|
76
|
+
# @option options [String] :name Readable name of the process
|
77
|
+
# @option options [String] :process_id ID of a process to be redeployed (do not set if you want to create a new process)
|
78
|
+
# @option options [Boolean] :verbose (false) Switch on verbose mode for detailed logging
|
79
|
+
def deploy(path, options = {})
|
80
|
+
path = Pathname(path) || fail('Path is not specified')
|
81
|
+
files_to_exclude = options[:files_to_exclude].nil? ? [] : options[:files_to_exclude].map { |p| Pathname(p) }
|
69
82
|
process_id = options[:process_id]
|
70
83
|
|
71
84
|
type = options[:type] || 'GRAPH'
|
72
85
|
deploy_name = options[:name]
|
73
86
|
verbose = options[:verbose] || false
|
74
|
-
puts HighLine.color("Deploying #{
|
75
|
-
deployed_path = Process.upload_package(
|
87
|
+
puts HighLine.color("Deploying #{path}", HighLine::BOLD) if verbose
|
88
|
+
deployed_path = Process.upload_package(path, files_to_exclude)
|
76
89
|
data = {
|
77
90
|
:process => {
|
78
91
|
:name => deploy_name,
|
79
|
-
:path => "/uploads/#{File.basename(deployed_path
|
92
|
+
:path => "/uploads/#{File.basename(deployed_path)}",
|
80
93
|
:type => type
|
81
94
|
}
|
82
95
|
}
|
@@ -86,7 +99,7 @@ module GoodData
|
|
86
99
|
GoodData.put("/gdc/projects/#{GoodData.project.pid}/dataload/processes/#{process_id}", data)
|
87
100
|
end
|
88
101
|
process = Process.new(res)
|
89
|
-
puts HighLine.color("Deploy DONE #{
|
102
|
+
puts HighLine.color("Deploy DONE #{path}", HighLine::GREEN) if verbose
|
90
103
|
process
|
91
104
|
end
|
92
105
|
end
|
@@ -99,10 +112,16 @@ module GoodData
|
|
99
112
|
GoodData.delete(uri)
|
100
113
|
end
|
101
114
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
115
|
+
# Redeploy existing process.
|
116
|
+
#
|
117
|
+
# @param path [String] Path to ZIP archive or to a directory containing files that should be ZIPed
|
118
|
+
# @option options [String] :files_to_exclude
|
119
|
+
# @option options [String] :process_id ('nobody') From address
|
120
|
+
# @option options [String] :type ('GRAPH') Type of process - GRAPH or RUBY
|
121
|
+
# @option options [String] :name Readable name of the process
|
122
|
+
# @option options [Boolean] :verbose (false) Switch on verbose mode for detailed logging
|
123
|
+
def deploy(path, options = {})
|
124
|
+
Process.deploy(path, options.merge(:process_id => process_id))
|
106
125
|
end
|
107
126
|
|
108
127
|
def process
|