gooddata 0.6.19 → 0.6.20
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +38 -0
- data/Rakefile +17 -3
- data/gooddata.gemspec +8 -7
- data/lib/gooddata/bricks/middleware/base_middleware.rb +1 -1
- data/lib/gooddata/cli/commands/run_ruby_cmd.rb +2 -2
- data/lib/gooddata/cli/shared.rb +2 -1
- data/lib/gooddata/commands/auth.rb +58 -5
- data/lib/gooddata/commands/runners.rb +2 -6
- data/lib/gooddata/extensions/big_decimal.rb +4 -0
- data/lib/gooddata/extensions/false.rb +11 -0
- data/lib/gooddata/extensions/hash.rb +6 -17
- data/lib/gooddata/extensions/nil.rb +11 -0
- data/lib/gooddata/extensions/numeric.rb +11 -0
- data/lib/gooddata/extensions/object.rb +11 -0
- data/lib/gooddata/extensions/symbol.rb +11 -0
- data/lib/gooddata/extensions/true.rb +11 -0
- data/lib/gooddata/helpers/auth_helpers.rb +32 -2
- data/lib/gooddata/helpers/data_helper.rb +1 -1
- data/lib/gooddata/helpers/global_helpers.rb +98 -31
- data/lib/gooddata/mixins/md_finders.rb +15 -15
- data/lib/gooddata/mixins/md_object_query.rb +12 -2
- data/lib/gooddata/models/blueprint/blueprint_field.rb +2 -2
- data/lib/gooddata/models/blueprint/dataset_blueprint.rb +2 -2
- data/lib/gooddata/models/blueprint/project_blueprint.rb +3 -3
- data/lib/gooddata/models/blueprint/schema_blueprint.rb +1 -1
- data/lib/gooddata/models/datawarehouse.rb +1 -0
- data/lib/gooddata/models/domain.rb +13 -16
- data/lib/gooddata/models/from_wire.rb +0 -2
- data/lib/gooddata/models/membership.rb +1 -1
- data/lib/gooddata/models/metadata/attribute.rb +1 -1
- data/lib/gooddata/models/metadata/dashboard.rb +1 -1
- data/lib/gooddata/models/metadata/dataset.rb +1 -1
- data/lib/gooddata/models/metadata/dimension.rb +1 -1
- data/lib/gooddata/models/metadata/fact.rb +1 -1
- data/lib/gooddata/models/metadata/label.rb +16 -17
- data/lib/gooddata/models/metadata/metric.rb +1 -1
- data/lib/gooddata/models/metadata/report.rb +1 -1
- data/lib/gooddata/models/metadata/report_definition.rb +7 -7
- data/lib/gooddata/models/metadata/variable.rb +1 -1
- data/lib/gooddata/models/model.rb +2 -2
- data/lib/gooddata/models/profile.rb +2 -2
- data/lib/gooddata/models/project.rb +21 -23
- data/lib/gooddata/models/project_role.rb +3 -3
- data/lib/gooddata/models/schedule.rb +18 -4
- data/lib/gooddata/models/user_filters/mandatory_user_filter.rb +12 -15
- data/lib/gooddata/models/user_filters/user_filter.rb +8 -8
- data/lib/gooddata/models/user_filters/user_filter_builder.rb +16 -13
- data/lib/gooddata/models/user_filters/variable_user_filter.rb +1 -1
- data/lib/gooddata/rest/client.rb +4 -2
- data/lib/gooddata/rest/connection.rb +37 -30
- data/lib/gooddata/rest/connections/rest_client_connection.rb +1 -1
- data/lib/gooddata/version.rb +1 -1
- data/spec/environment/develop.rb +4 -4
- data/spec/environment/hotfix.rb +1 -1
- data/spec/environment/release.rb +1 -1
- data/spec/integration/full_project_spec.rb +3 -3
- data/spec/integration/over_to_user_filters_spec.rb +1 -0
- data/spec/integration/project_spec.rb +1 -1
- data/spec/integration/user_filters_spec.rb +0 -1
- data/spec/unit/commands/command_auth_spec.rb +10 -0
- data/spec/unit/extensions/hash_spec.rb +1 -1
- data/spec/unit/helpers_spec.rb +0 -8
- data/spec/unit/models/domain_spec.rb +1 -9
- data/spec/unit/models/from_wire_spec.rb +1 -19
- data/spec/unit/models/membership_spec.rb +1 -1
- data/spec/unit/models/metadata_spec.rb +1 -1
- data/spec/unit/models/profile_spec.rb +23 -47
- data/spec/unit/models/schedule_spec.rb +47 -3
- metadata +174 -50
- data/lib/gooddata/models/from_wire_parse.rb +0 -125
@@ -203,7 +203,11 @@ module GoodData
|
|
203
203
|
label.find_value_uri(v)
|
204
204
|
end
|
205
205
|
rescue
|
206
|
-
errors <<
|
206
|
+
errors << {
|
207
|
+
type: :error,
|
208
|
+
label: label.title,
|
209
|
+
value: v
|
210
|
+
}
|
207
211
|
nil
|
208
212
|
end
|
209
213
|
end
|
@@ -226,10 +230,10 @@ module GoodData
|
|
226
230
|
# Encapuslates the creation of filter
|
227
231
|
def self.create_user_filter(expression, related)
|
228
232
|
{
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
+
related: related,
|
234
|
+
level: :user,
|
235
|
+
expression: expression,
|
236
|
+
type: :filter
|
233
237
|
}
|
234
238
|
end
|
235
239
|
|
@@ -251,21 +255,21 @@ module GoodData
|
|
251
255
|
lookups_cache = create_lookups_cache(small_labels)
|
252
256
|
attrs_cache = create_attrs_cache(filters, options)
|
253
257
|
|
254
|
-
|
255
|
-
results = filters.pmapcat do |filter|
|
258
|
+
results = filters.flat_map do |filter|
|
256
259
|
login = filter[:login]
|
257
260
|
filter[:filters].pmapcat do |f|
|
258
|
-
expression,
|
259
|
-
errors << error unless error.empty?
|
261
|
+
expression, errors = create_expression(f, labels_cache, lookups_cache, attrs_cache, options)
|
260
262
|
profiles_uri = (users_cache[login] && users_cache[login].uri)
|
261
263
|
if profiles_uri && expression
|
262
|
-
[create_user_filter(expression, profiles_uri)]
|
264
|
+
[create_user_filter(expression, profiles_uri)] + errors
|
263
265
|
else
|
264
|
-
[]
|
266
|
+
[] + errors
|
265
267
|
end
|
266
268
|
end
|
267
269
|
end
|
268
|
-
|
270
|
+
results.group_by { |i| i[:type] }
|
271
|
+
.values_at(:filter, :error)
|
272
|
+
.map { |i| i || [] }
|
269
273
|
end
|
270
274
|
|
271
275
|
def self.resolve_user_filter(user = [], project = [])
|
@@ -436,7 +440,6 @@ module GoodData
|
|
436
440
|
end
|
437
441
|
|
438
442
|
fail "Validation failed #{errors}" if !ignore_missing_values && !errors.empty?
|
439
|
-
|
440
443
|
filters = user_filters.map { |data| client.create(klass, data, project: project) }
|
441
444
|
resolve_user_filters(filters, project_filters)
|
442
445
|
end
|
data/lib/gooddata/rest/client.rb
CHANGED
@@ -67,7 +67,7 @@ module GoodData
|
|
67
67
|
password = ENV['GD_GEM_PASSWORD']
|
68
68
|
end
|
69
69
|
|
70
|
-
username =
|
70
|
+
username = GoodData::Helpers.symbolize_keys(username) if username.is_a?(Hash)
|
71
71
|
|
72
72
|
new_opts = opts.dup
|
73
73
|
if username.is_a?(Hash) && username.key?(:sst_token)
|
@@ -144,7 +144,7 @@ module GoodData
|
|
144
144
|
@factory = ObjectFactory.new(self)
|
145
145
|
end
|
146
146
|
|
147
|
-
def create_project(options = { title: 'Project'
|
147
|
+
def create_project(options = { title: 'Project' })
|
148
148
|
GoodData::Project.create({ client: self }.merge(options))
|
149
149
|
end
|
150
150
|
|
@@ -246,6 +246,8 @@ module GoodData
|
|
246
246
|
|
247
247
|
url = project.links['uploads']
|
248
248
|
fail 'Project WebDAV not supported in this Data Center' unless url
|
249
|
+
|
250
|
+
GoodData.logger.warn 'Beware! Project webdav is deprecated and should not be used.'
|
249
251
|
url
|
250
252
|
end
|
251
253
|
|
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
require 'terminal-table'
|
4
4
|
require 'securerandom'
|
5
|
+
require 'monitor'
|
6
|
+
require 'thread_safe'
|
5
7
|
|
6
8
|
require_relative '../version'
|
7
9
|
require_relative '../exceptions/exceptions'
|
@@ -10,7 +12,9 @@ module GoodData
|
|
10
12
|
module Rest
|
11
13
|
# Wrapper of low-level HTTP/REST client/library
|
12
14
|
class Connection
|
13
|
-
|
15
|
+
include MonitorMixin
|
16
|
+
|
17
|
+
DEFAULT_URL = 'https://secure.gooddata.com'
|
14
18
|
LOGIN_PATH = '/gdc/account/login'
|
15
19
|
TOKEN_PATH = '/gdc/account/token'
|
16
20
|
KEYS_TO_SCRUB = [:password, :verifyPassword, :authorizationToken]
|
@@ -98,7 +102,8 @@ module GoodData
|
|
98
102
|
attr_reader :user
|
99
103
|
|
100
104
|
def initialize(opts)
|
101
|
-
|
105
|
+
super()
|
106
|
+
@stats = ThreadSafe::Hash.new
|
102
107
|
|
103
108
|
headers = opts[:headers] || {}
|
104
109
|
@webdav_headers = DEFAULT_WEBDAV_HEADERS.merge(headers)
|
@@ -115,7 +120,7 @@ module GoodData
|
|
115
120
|
|
116
121
|
# Connect using username and password
|
117
122
|
def connect(username, password, options = {})
|
118
|
-
server = options[:server] ||
|
123
|
+
server = options[:server] || Helpers::AuthHelper.read_server
|
119
124
|
options = DEFAULT_LOGIN_PAYLOAD.merge(options)
|
120
125
|
headers = options[:headers] || {}
|
121
126
|
|
@@ -278,10 +283,10 @@ module GoodData
|
|
278
283
|
# @param uri [String] Target URI
|
279
284
|
def get(uri, options = {}, &user_block)
|
280
285
|
options = log_info(options)
|
281
|
-
GoodData.logger.debug "GET: #{@server.url}#{uri}"
|
286
|
+
GoodData.logger.debug "GET: #{@server.url}#{uri}, #{options}"
|
282
287
|
profile "GET #{uri}" do
|
283
288
|
b = proc do
|
284
|
-
params = fresh_request_params(options[:request_id])
|
289
|
+
params = fresh_request_params(options[:request_id]).merge(options)
|
285
290
|
begin
|
286
291
|
@server[uri].get(params, &user_block)
|
287
292
|
rescue RestClient::Exception => e
|
@@ -526,7 +531,7 @@ module GoodData
|
|
526
531
|
def scrub_params(params, keys)
|
527
532
|
keys = keys.reduce([]) { |a, e| a.concat([e.to_s, e.to_sym]) }
|
528
533
|
|
529
|
-
new_params =
|
534
|
+
new_params = GoodData::Helpers.deep_dup(params)
|
530
535
|
GoodData::Helpers.hash_dfs(new_params) do |k, _key|
|
531
536
|
keys.each do |key_to_scrub|
|
532
537
|
k[key_to_scrub] = ('*' * k[key_to_scrub].length) if k && k.key?(key_to_scrub) && k[key_to_scrub]
|
@@ -558,37 +563,39 @@ module GoodData
|
|
558
563
|
]
|
559
564
|
|
560
565
|
def update_stats(title, delta)
|
561
|
-
|
566
|
+
synchronize do
|
567
|
+
orig_title = title
|
562
568
|
|
563
|
-
|
569
|
+
placeholders = true
|
564
570
|
|
565
|
-
|
566
|
-
|
567
|
-
|
571
|
+
if placeholders
|
572
|
+
PH_MAP.each do |pm|
|
573
|
+
break if title.gsub!(pm[1], pm[0])
|
574
|
+
end
|
568
575
|
end
|
569
|
-
end
|
570
576
|
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
577
|
+
stat = stats[title]
|
578
|
+
if stat.nil?
|
579
|
+
stat = {
|
580
|
+
:min => delta,
|
581
|
+
:max => delta,
|
582
|
+
:total => 0,
|
583
|
+
:avg => 0,
|
584
|
+
:calls => 0,
|
585
|
+
:entries => []
|
586
|
+
}
|
587
|
+
end
|
582
588
|
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
589
|
+
stat[:min] = delta if delta < stat[:min]
|
590
|
+
stat[:max] = delta if delta > stat[:max]
|
591
|
+
stat[:total] += delta
|
592
|
+
stat[:calls] += 1
|
593
|
+
stat[:avg] = stat[:total] / stat[:calls]
|
588
594
|
|
589
|
-
|
595
|
+
stat[:entries] << orig_title if placeholders
|
590
596
|
|
591
|
-
|
597
|
+
stats[title] = stat
|
598
|
+
end
|
592
599
|
end
|
593
600
|
|
594
601
|
def webdav_dir_exists?(url)
|
@@ -24,7 +24,7 @@ module GoodData
|
|
24
24
|
|
25
25
|
# Connect using username and password
|
26
26
|
def connect(username, password, options = {})
|
27
|
-
server = options[:server] ||
|
27
|
+
server = options[:server] || Helpers::AuthHelper.read_server
|
28
28
|
@server = RestClient::Resource.new server, DEFAULT_LOGIN_PAYLOAD
|
29
29
|
|
30
30
|
super
|
data/lib/gooddata/version.rb
CHANGED
data/spec/environment/develop.rb
CHANGED
@@ -8,19 +8,19 @@ module GoodData
|
|
8
8
|
end
|
9
9
|
|
10
10
|
module ProcessHelper
|
11
|
-
PROCESS_ID = '
|
12
|
-
DEPLOY_NAME = 'graph.grf'
|
11
|
+
PROCESS_ID = '94e7be67-5f68-405d-bdeb-93006d50482d'
|
12
|
+
DEPLOY_NAME = 'graph/graph.grf'
|
13
13
|
end
|
14
14
|
|
15
15
|
module ProjectHelper
|
16
|
-
PROJECT_ID = '
|
16
|
+
PROJECT_ID = 'k8rzngunca3t9dywmxhqzpgwlui3yg0m'
|
17
17
|
PROJECT_URL = "/gdc/projects/#{PROJECT_ID}"
|
18
18
|
PROJECT_TITLE = 'GoodTravis'
|
19
19
|
PROJECT_SUMMARY = 'No summary'
|
20
20
|
end
|
21
21
|
|
22
22
|
module ScheduleHelper
|
23
|
-
SCHEDULE_ID = '
|
23
|
+
SCHEDULE_ID = '55953261e4b0a92792febe4e'
|
24
24
|
end
|
25
25
|
end
|
26
26
|
end
|
data/spec/environment/hotfix.rb
CHANGED
data/spec/environment/release.rb
CHANGED
@@ -260,8 +260,8 @@ describe "Full project implementation", :constraint => 'slow' do
|
|
260
260
|
end
|
261
261
|
|
262
262
|
it "should be possible to get all metrics with full objects" do
|
263
|
-
|
264
|
-
expect(
|
263
|
+
metrics = @project.metrics(:all)
|
264
|
+
expect(metrics.first.class).to be GoodData::Metric
|
265
265
|
end
|
266
266
|
|
267
267
|
it "should be able to get a metric by identifier" do
|
@@ -412,7 +412,7 @@ describe "Full project implementation", :constraint => 'slow' do
|
|
412
412
|
attribute = @project.attributes('attr.devs.dev_id')
|
413
413
|
label = attribute.primary_label
|
414
414
|
value = label.values.first
|
415
|
-
different_value = label.values
|
415
|
+
different_value = label.values.drop(1).first
|
416
416
|
fact = @project.facts('fact.commits.lines_changed')
|
417
417
|
metric = @project.create_metric("SELECT SUM([#{fact.uri}]) WHERE [#{attribute.uri}] = [#{value[:uri]}]")
|
418
418
|
metric.replace_value(label, value[:value], different_value[:value])
|
@@ -2,6 +2,7 @@ require 'gooddata'
|
|
2
2
|
|
3
3
|
describe "Variables implementation", :constraint => 'slow' do
|
4
4
|
before(:all) do
|
5
|
+
GoodData.logging_http_on
|
5
6
|
@spec = JSON.parse(File.read("./spec/data/blueprints/m_n_model.json"), :symbolize_names => true)
|
6
7
|
@client = ConnectionHelper::create_default_connection
|
7
8
|
@blueprint = GoodData::Model::ProjectBlueprint.new(@spec)
|
@@ -132,7 +132,7 @@ describe GoodData::Project, :constraint => 'slow' do
|
|
132
132
|
|
133
133
|
describe '#set_user_roles' do
|
134
134
|
it 'Properly updates user roles as needed' do
|
135
|
-
users_to_import = @domain.users.
|
135
|
+
users_to_import = @domain.users.drop(rand(100)).take(5).map {|u| { user: u, role: 'admin' }}
|
136
136
|
@project.import_users(users_to_import, domain: @domain, whitelists: [/gem_tester@gooddata.com/])
|
137
137
|
users_without_owner = @project.users.reject { |u| u.login == ConnectionHelper::DEFAULT_USERNAME }.pselect { |u| u.role.title == 'Admin' }
|
138
138
|
|
@@ -44,7 +44,6 @@ describe "User filters implementation", :constraint => 'slow' do
|
|
44
44
|
@project.add_data_permissions(filters)
|
45
45
|
metric.execute.should == 6
|
46
46
|
r = @project.compute_report(left: [metric], top: [@label.attribute])
|
47
|
-
r.include_column?(['tomas@gooddata.com', 1]).should == true
|
48
47
|
|
49
48
|
r.include_column?(['tomas@gooddata.com', 1]).should == true
|
50
49
|
r.include_column?(['jirka@gooddata.com', 5]).should == true
|
@@ -11,6 +11,8 @@ describe GoodData::Command::Auth do
|
|
11
11
|
:email => 'joedoe@example.com',
|
12
12
|
:password => 'secretPassword',
|
13
13
|
:token => 't0k3n1sk0',
|
14
|
+
:environment => 'DEVELOPMENT',
|
15
|
+
:server => 'https://secure.gooddata.com'
|
14
16
|
}
|
15
17
|
|
16
18
|
DEFAULT_CREDENTIALS_OVER = {
|
@@ -59,6 +61,8 @@ describe GoodData::Command::Auth do
|
|
59
61
|
@input << DEFAULT_CREDENTIALS[:email] << "\n"
|
60
62
|
@input << DEFAULT_CREDENTIALS[:password] << "\n"
|
61
63
|
@input << DEFAULT_CREDENTIALS[:token] << "\n"
|
64
|
+
@input << DEFAULT_CREDENTIALS[:environment] << "\n"
|
65
|
+
@input << DEFAULT_CREDENTIALS[:server] << "\n"
|
62
66
|
@input.rewind
|
63
67
|
|
64
68
|
GoodData::Command::Auth.ask_for_credentials
|
@@ -105,6 +109,8 @@ describe GoodData::Command::Auth do
|
|
105
109
|
@input << DEFAULT_CREDENTIALS[:email] << "\n"
|
106
110
|
@input << DEFAULT_CREDENTIALS[:password] << "\n"
|
107
111
|
@input << DEFAULT_CREDENTIALS[:token] << "\n"
|
112
|
+
@input << DEFAULT_CREDENTIALS[:environment] << "\n"
|
113
|
+
@input << DEFAULT_CREDENTIALS[:server] << "\n"
|
108
114
|
@input << 'y' << "\n"
|
109
115
|
@input.rewind
|
110
116
|
|
@@ -118,6 +124,8 @@ describe GoodData::Command::Auth do
|
|
118
124
|
@input << DEFAULT_CREDENTIALS[:email] << "\n"
|
119
125
|
@input << DEFAULT_CREDENTIALS[:password] << "\n"
|
120
126
|
@input << DEFAULT_CREDENTIALS[:token] << "\n"
|
127
|
+
@input << DEFAULT_CREDENTIALS[:environment] << "\n"
|
128
|
+
@input << DEFAULT_CREDENTIALS[:server] << "\n"
|
121
129
|
@input << 'y' << "\n"
|
122
130
|
@input.rewind
|
123
131
|
|
@@ -132,6 +140,8 @@ describe GoodData::Command::Auth do
|
|
132
140
|
@input << DEFAULT_CREDENTIALS_OVER[:email] << "\n"
|
133
141
|
@input << DEFAULT_CREDENTIALS_OVER[:password] << "\n"
|
134
142
|
@input << DEFAULT_CREDENTIALS_OVER[:token] << "\n"
|
143
|
+
@input << DEFAULT_CREDENTIALS[:environment] << "\n"
|
144
|
+
@input << DEFAULT_CREDENTIALS[:server] << "\n"
|
135
145
|
@input << 'n' << "\n"
|
136
146
|
@input.rewind
|
137
147
|
|
data/spec/unit/helpers_spec.rb
CHANGED
@@ -37,14 +37,6 @@ describe GoodData::Helpers do
|
|
37
37
|
end
|
38
38
|
end
|
39
39
|
|
40
|
-
describe '#sanitize_string' do
|
41
|
-
it 'works' do
|
42
|
-
expect = 'helloworld'
|
43
|
-
result = GoodData::Helpers.sanitize_string('Hello World')
|
44
|
-
result.should == expect
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
40
|
describe "#decode_params" do
|
49
41
|
it 'decodes the data params from json' do
|
50
42
|
params = {
|
@@ -57,15 +57,7 @@ describe GoodData::Domain do
|
|
57
57
|
describe '#users' do
|
58
58
|
it 'Should list users' do
|
59
59
|
users = @domain.users
|
60
|
-
expect(users).to be_instance_of(
|
61
|
-
users.each do |user|
|
62
|
-
expect(user).to be_an_instance_of(GoodData::Profile)
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
it 'Accepts pagination options - limit' do
|
67
|
-
users = @domain.users(:all, limit: 10)
|
68
|
-
expect(users).to be_instance_of(Array)
|
60
|
+
expect(users).to be_instance_of(Enumerator)
|
69
61
|
users.each do |user|
|
70
62
|
expect(user).to be_an_instance_of(GoodData::Profile)
|
71
63
|
end
|
@@ -287,25 +287,7 @@ describe GoodData::Model::FromWire do
|
|
287
287
|
expect(GoodData::Model.check_gd_data_type("decimal(10,5)")).to eq true
|
288
288
|
expect(GoodData::Model.check_gd_data_type("decimal(10, 5)")).to eq true
|
289
289
|
end
|
290
|
-
|
291
|
-
# it "should be able to omit titles if they are superfluous" do
|
292
|
-
# view = MultiJson.load(File.read('./spec/data/superfluous_titles_view.json'))
|
293
|
-
# blueprint = FromWire.from_wire(view)
|
294
|
-
# expect(blueprint.datasets.count).to eq 1
|
295
|
-
# expect(blueprint.datasets.first.find_column_by_name('current_status', nil).key?(:title)).to eq false
|
296
|
-
# expect(blueprint.datasets.mapcat { |ds| ds.columns }.any? {|col| col[:name].titleize == col[:title]}).to eq false
|
297
|
-
# end
|
298
|
-
#
|
299
|
-
# it "should enable sorting" do
|
300
|
-
# skip("UAAA")
|
301
|
-
# end
|
302
|
-
#
|
303
|
-
# it "should generate the same thing it parsed" do
|
304
|
-
# a = @model_view['projectModelView']['model']['projectModel']['datasets'][3]
|
305
|
-
# b = @blueprint.to_wire
|
306
|
-
# # expect(b).to eq a
|
307
|
-
# end
|
308
|
-
#
|
290
|
+
|
309
291
|
it "should be able to parse description from both attributes and facts" do
|
310
292
|
expect(@blueprint.find_dataset('dataset.opportunity').anchor.description).to eq 'This is opportunity attribute description'
|
311
293
|
expect(@blueprint.find_dataset('dataset.stage_history').facts.find {|f| f.id == 'fact.stage_history.stage_velocity'}.description).to eq 'Velocity description'
|