gooddata 2.1.8-java → 2.1.13-java
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.
- checksums.yaml +4 -4
- data/.rubocop.yml +7 -0
- data/.travis.yml +2 -4
- data/CHANGELOG.md +43 -0
- data/Dockerfile +19 -4
- data/Dockerfile.jruby +4 -4
- data/Dockerfile.ruby +5 -4
- data/README.md +2 -0
- data/SDK_VERSION +1 -1
- data/VERSION +1 -1
- data/bin/provision.sh +2 -0
- data/bin/release.sh +2 -0
- data/bin/rollout.sh +2 -0
- data/bin/run_brick.rb +31 -7
- data/bin/test_projects_cleanup.rb +10 -2
- data/bin/user_filters.sh +2 -0
- data/ci.rake +1 -1
- data/ci/bigquery/pom.xml +54 -0
- data/ci/redshift/pom.xml +73 -0
- data/ci/snowflake/pom.xml +57 -0
- data/dev-gooddata-sso.pub.encrypted +40 -40
- data/gdc_fossa_lcm.yaml +2 -0
- data/gdc_fossa_ruby_sdk.yaml +4 -0
- data/gooddata.gemspec +6 -2
- data/k8s/charts/lcm-bricks/Chart.yaml +1 -1
- data/k8s/charts/lcm-bricks/templates/prometheus/alertingRules.yaml +22 -12
- data/lcm.rake +14 -0
- data/lib/gooddata/bricks/middleware/execution_result_middleware.rb +68 -0
- data/lib/gooddata/bricks/middleware/logger_middleware.rb +2 -1
- data/lib/gooddata/bricks/middleware/mask_logger_decorator.rb +5 -1
- data/lib/gooddata/bricks/pipeline.rb +7 -0
- data/lib/gooddata/cloud_resources/bigquery/bigquery_client.rb +86 -0
- data/lib/gooddata/cloud_resources/bigquery/drivers/.gitkeepme +0 -0
- data/lib/gooddata/cloud_resources/cloud_resouce_factory.rb +28 -0
- data/lib/gooddata/cloud_resources/cloud_resource_client.rb +24 -0
- data/lib/gooddata/cloud_resources/cloud_resources.rb +12 -0
- data/lib/gooddata/cloud_resources/redshift/drivers/log4j.properties +15 -0
- data/lib/gooddata/cloud_resources/redshift/redshift_client.rb +101 -0
- data/lib/gooddata/cloud_resources/snowflake/drivers/.gitkeepme +0 -0
- data/lib/gooddata/cloud_resources/snowflake/snowflake_client.rb +84 -0
- data/lib/gooddata/exceptions/invalid_env_error.rb +15 -0
- data/lib/gooddata/helpers/data_helper.rb +10 -0
- data/lib/gooddata/helpers/data_source_helpers.rb +47 -0
- data/lib/gooddata/helpers/global_helpers.rb +4 -0
- data/lib/gooddata/helpers/global_helpers_params.rb +6 -9
- data/lib/gooddata/lcm/actions/collect_clients.rb +6 -6
- data/lib/gooddata/lcm/actions/collect_dynamic_schedule_params.rb +6 -6
- data/lib/gooddata/lcm/actions/collect_segment_clients.rb +4 -1
- data/lib/gooddata/lcm/actions/collect_segments.rb +1 -2
- data/lib/gooddata/lcm/actions/collect_users_brick_users.rb +7 -6
- data/lib/gooddata/lcm/actions/create_segment_masters.rb +5 -3
- data/lib/gooddata/lcm/actions/migrate_gdc_date_dimension.rb +116 -0
- data/lib/gooddata/lcm/actions/set_master_project.rb +76 -0
- data/lib/gooddata/lcm/actions/synchronize_clients.rb +1 -1
- data/lib/gooddata/lcm/actions/synchronize_etls_in_segment.rb +1 -2
- data/lib/gooddata/lcm/actions/synchronize_ldm.rb +20 -3
- data/lib/gooddata/lcm/actions/synchronize_user_filters.rb +23 -3
- data/lib/gooddata/lcm/actions/synchronize_users.rb +50 -30
- data/lib/gooddata/lcm/actions/update_release_table.rb +7 -1
- data/lib/gooddata/lcm/exceptions/lcm_execution_error.rb +16 -0
- data/lib/gooddata/lcm/helpers/release_table_helper.rb +16 -8
- data/lib/gooddata/lcm/lcm2.rb +28 -5
- data/lib/gooddata/models/domain.rb +17 -15
- data/lib/gooddata/models/execution.rb +0 -1
- data/lib/gooddata/models/execution_detail.rb +0 -1
- data/lib/gooddata/models/from_wire.rb +1 -0
- data/lib/gooddata/models/process.rb +11 -3
- data/lib/gooddata/models/profile.rb +33 -11
- data/lib/gooddata/models/project.rb +120 -31
- data/lib/gooddata/models/project_creator.rb +2 -0
- data/lib/gooddata/models/schedule.rb +0 -1
- data/lib/gooddata/rest/client.rb +2 -2
- data/lib/gooddata/rest/connection.rb +5 -3
- data/rubydev_public.gpg.encrypted +51 -51
- data/rubydev_secret_keys.gpg.encrypted +109 -109
- metadata +32 -13
- data/lib/gooddata/extensions/hash.rb +0 -18
@@ -324,20 +324,42 @@ module GoodData
|
|
324
324
|
end
|
325
325
|
|
326
326
|
# Gets the array of projects
|
327
|
-
#
|
327
|
+
# @param limit [Integer] maximum number of projects to get.
|
328
|
+
# @param offset [Integer] offset of the first project, start from 0.
|
328
329
|
# @return [Array<GoodData::Project>] Array of project where account settings belongs to
|
329
|
-
def projects(limit = nil)
|
330
|
+
def projects(limit = nil, offset = nil)
|
330
331
|
url = @json['accountSetting']['links']['projects']
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
332
|
+
|
333
|
+
all_projects = []
|
334
|
+
|
335
|
+
raise ArgumentError, 'Params limit and offset are expected' if !offset.nil? && limit.nil?
|
336
|
+
|
337
|
+
if limit.nil?
|
338
|
+
url += "?limit=500"
|
339
|
+
loop do
|
340
|
+
projects = client.get url
|
341
|
+
projects['projects']['items'].each do |project|
|
342
|
+
all_projects << client.create(GoodData::Project, project)
|
343
|
+
end
|
344
|
+
if !projects['projects']['paging'].nil? && !projects['projects']['paging']['next'].nil?
|
345
|
+
url = projects['projects']['paging']['next']
|
346
|
+
else
|
347
|
+
break
|
348
|
+
end
|
349
|
+
end
|
350
|
+
else
|
351
|
+
limit = [limit, 500].min if limit.is_a?(Integer) && limit > 0
|
352
|
+
|
353
|
+
url += "?limit=#{limit}"
|
354
|
+
url += "&offset=#{offset}" if !offset.nil? && offset.is_a?(Integer) && offset > 0
|
355
|
+
|
356
|
+
projects = client.get url
|
357
|
+
projects['projects']['items'].each do |project|
|
358
|
+
all_projects << client.create(GoodData::Project, project)
|
359
|
+
end
|
340
360
|
end
|
361
|
+
|
362
|
+
all_projects
|
341
363
|
end
|
342
364
|
|
343
365
|
# Saves object if dirty, clears dirty flag
|
@@ -67,9 +67,9 @@ module GoodData
|
|
67
67
|
class << self
|
68
68
|
# Returns an array of all projects accessible by
|
69
69
|
# current user
|
70
|
-
def all(opts = { client: GoodData.connection }, limit = nil)
|
70
|
+
def all(opts = { client: GoodData.connection }, limit = nil, offset = nil)
|
71
71
|
c = GoodData.get_client(opts)
|
72
|
-
c.user.projects(limit)
|
72
|
+
c.user.projects(limit, offset)
|
73
73
|
end
|
74
74
|
|
75
75
|
# Returns a Project object identified by given string
|
@@ -261,21 +261,26 @@ module GoodData
|
|
261
261
|
# @option ads_output_stage_uri Uri of the source output stage. It must be in the same domain as the target project.
|
262
262
|
def transfer_processes(from_project, to_project, options = {})
|
263
263
|
options = GoodData::Helpers.symbolize_keys(options)
|
264
|
+
aliases = {}
|
264
265
|
to_project_processes = to_project.processes
|
265
266
|
additional_hidden_params = options[:additional_hidden_params] || {}
|
266
267
|
result = from_project.processes.uniq(&:name).map do |process|
|
267
|
-
fail "The process name #{process.name} must be unique in
|
268
|
+
fail "The process name #{process.name} must be unique in transferred project #{to_project}" if to_project_processes.count { |p| p.name == process.name } > 1
|
268
269
|
next if process.type == :dataload || process.add_v2_component?
|
270
|
+
collect_process_aliases(process.data, from_project.client, aliases)
|
269
271
|
|
270
272
|
to_process = to_project_processes.find { |p| p.name == process.name }
|
271
273
|
|
274
|
+
data_sources = GoodData::Helpers.symbolize_keys_recursively!(process.data_sources)
|
275
|
+
data_sources = replace_data_source_ids(data_sources, to_project.client, aliases)
|
272
276
|
to_process = if process.path
|
273
277
|
to_process.delete if to_process
|
274
|
-
|
278
|
+
Process.deploy_from_appstore(process.path, name: process.name, client: to_project.client, project: to_project, data_sources: data_sources)
|
275
279
|
elsif process.component
|
276
280
|
to_process.delete if to_process
|
277
281
|
process_hash = GoodData::Helpers::DeepMergeableHash[GoodData::Helpers.symbolize_keys(process.to_hash)].deep_merge(additional_hidden_params)
|
278
|
-
|
282
|
+
process_hash = replace_process_data_source_ids(process_hash, to_project.client, aliases)
|
283
|
+
Process.deploy_component(process_hash, project: to_project, client: to_project.client)
|
279
284
|
else
|
280
285
|
Dir.mktmpdir('etl_transfer') do |dir|
|
281
286
|
dir = Pathname(dir)
|
@@ -283,11 +288,10 @@ module GoodData
|
|
283
288
|
File.open(filename, 'w') do |f|
|
284
289
|
f << process.download
|
285
290
|
end
|
286
|
-
|
287
291
|
if to_process
|
288
|
-
to_process.deploy(filename, type: process.type, name: process.name)
|
292
|
+
to_process.deploy(filename, type: process.type, name: process.name, data_sources: data_sources)
|
289
293
|
else
|
290
|
-
to_project.deploy_process(filename, type: process.type, name: process.name)
|
294
|
+
to_project.deploy_process(filename, type: process.type, name: process.name, data_sources: data_sources)
|
291
295
|
end
|
292
296
|
end
|
293
297
|
end
|
@@ -318,6 +322,56 @@ module GoodData
|
|
318
322
|
result.compact
|
319
323
|
end
|
320
324
|
|
325
|
+
def collect_process_aliases(process_data, client, aliases)
|
326
|
+
data_sources = process_data.dig('process', 'dataSources')
|
327
|
+
unless data_sources.blank?
|
328
|
+
data_sources.map do |data_source|
|
329
|
+
get_data_source_alias(data_source['id'], client, aliases)
|
330
|
+
end
|
331
|
+
end
|
332
|
+
component = process_data.dig('process', 'component')
|
333
|
+
get_data_source_alias(component['configLocation']['dataSourceConfig']['id'], client, aliases) if component&.dig('configLocation', 'dataSourceConfig')
|
334
|
+
aliases
|
335
|
+
end
|
336
|
+
|
337
|
+
def get_data_source_alias(data_source_id, client, aliases)
|
338
|
+
unless aliases[data_source_id]
|
339
|
+
data_source = GoodData::Helpers.get_data_source_by_id(data_source_id, client)
|
340
|
+
if data_source&.dig('dataSource', 'alias')
|
341
|
+
aliases[data_source_id] = {
|
342
|
+
:type => get_data_source_type(data_source),
|
343
|
+
:alias => data_source['dataSource']['alias']
|
344
|
+
}
|
345
|
+
end
|
346
|
+
end
|
347
|
+
aliases[data_source_id]
|
348
|
+
end
|
349
|
+
|
350
|
+
def get_data_source_type(data_source_data)
|
351
|
+
data_source_data&.dig('dataSource', 'connectionInfo') ? data_source_data['dataSource']['connectionInfo'].first[0].upcase : ""
|
352
|
+
end
|
353
|
+
|
354
|
+
def replace_process_data_source_ids(process_data, client, aliases)
|
355
|
+
component = process_data.dig(:process, :component)
|
356
|
+
if component&.dig(:configLocation, :dataSourceConfig)
|
357
|
+
the_alias = aliases[component[:configLocation][:dataSourceConfig][:id]]
|
358
|
+
process_data[:process][:component][:configLocation][:dataSourceConfig][:id] = GoodData::Helpers.verify_data_source_alias(the_alias, client)
|
359
|
+
end
|
360
|
+
process_data[:process][:dataSources] = replace_data_source_ids(process_data[:process][:dataSources], client, aliases)
|
361
|
+
process_data
|
362
|
+
end
|
363
|
+
|
364
|
+
def replace_data_source_ids(data_sources, client, aliases)
|
365
|
+
array_data_sources = []
|
366
|
+
if data_sources && !data_sources.empty?
|
367
|
+
data_sources.map do |data_source|
|
368
|
+
new_id = GoodData::Helpers.verify_data_source_alias(aliases[data_source[:id]], client)
|
369
|
+
array_data_sources.push(:id => new_id)
|
370
|
+
end
|
371
|
+
end
|
372
|
+
array_data_sources
|
373
|
+
end
|
374
|
+
|
321
375
|
def transfer_user_groups(from_project, to_project)
|
322
376
|
from_project.user_groups.map do |ug|
|
323
377
|
# migrate groups
|
@@ -625,6 +679,7 @@ module GoodData
|
|
625
679
|
def blueprint(options = {})
|
626
680
|
options = { include_ca: true }.merge(options)
|
627
681
|
result = client.get("/gdc/projects/#{pid}/model/view", params: { includeDeprecated: true, includeGrain: true, includeCA: options[:include_ca] })
|
682
|
+
|
628
683
|
polling_url = result['asyncTask']['link']['poll']
|
629
684
|
model = client.poll_on_code(polling_url, options)
|
630
685
|
bp = GoodData::Model::FromWire.from_wire(model, options)
|
@@ -1546,26 +1601,28 @@ module GoodData
|
|
1546
1601
|
# @return [Array<GoodData::User>] List of users
|
1547
1602
|
def users(opts = {})
|
1548
1603
|
client = client(opts)
|
1549
|
-
|
1550
|
-
|
1551
|
-
|
1552
|
-
|
1553
|
-
|
1554
|
-
|
1555
|
-
|
1556
|
-
|
1557
|
-
|
1558
|
-
|
1559
|
-
|
1560
|
-
|
1561
|
-
|
1562
|
-
|
1563
|
-
end
|
1604
|
+
all_users = []
|
1605
|
+
offset = opts[:offset] || 0
|
1606
|
+
limit = opts[:limit] || 1_000
|
1607
|
+
loop do
|
1608
|
+
tmp = client.get("/gdc/projects/#{pid}/users", params: { offset: offset, limit: limit })
|
1609
|
+
tmp['users'].each do |user_data|
|
1610
|
+
user = client.create(GoodData::Membership, user_data, project: self)
|
1611
|
+
|
1612
|
+
if opts[:all]
|
1613
|
+
all_users << user
|
1614
|
+
elsif opts[:disabled]
|
1615
|
+
all_users << user if user&.disabled?
|
1616
|
+
else
|
1617
|
+
all_users << user if user&.enabled?
|
1564
1618
|
end
|
1565
|
-
break if tmp['users'].count < limit
|
1566
|
-
offset += limit
|
1567
1619
|
end
|
1620
|
+
break if tmp['users'].count < limit
|
1621
|
+
|
1622
|
+
offset += limit
|
1568
1623
|
end
|
1624
|
+
|
1625
|
+
all_users
|
1569
1626
|
end
|
1570
1627
|
|
1571
1628
|
alias_method :members, :users
|
@@ -1604,14 +1661,19 @@ module GoodData
|
|
1604
1661
|
def import_users(new_users, options = {})
|
1605
1662
|
role_list = roles
|
1606
1663
|
users_list = users
|
1607
|
-
new_users = new_users.map { |x| ((x.is_a?(Hash) && x[:user] && x[:user].to_hash.merge(role: x[:role])) || x.to_hash).tap { |u| u[:login].downcase! } }
|
1608
1664
|
|
1609
1665
|
GoodData.logger.warn("Importing users to project (#{pid})")
|
1666
|
+
new_users = new_users.map { |x| ((x.is_a?(Hash) && x[:user] && x[:user].to_hash.merge(role: x[:role])) || x.to_hash).tap { |u| u[:login].downcase! } }
|
1667
|
+
# First check that if groups are provided we have them set up
|
1668
|
+
user_groups_cache, change_groups = check_groups(new_users.map(&:to_hash).flat_map { |u| u[:user_group] || [] }.uniq, options[:user_groups_cache], options)
|
1610
1669
|
|
1611
|
-
|
1670
|
+
unless change_groups.empty?
|
1671
|
+
new_users.each do |user|
|
1672
|
+
user[:user_group].map! { |e| change_groups[e].nil? ? e : change_groups[e] }
|
1673
|
+
end
|
1674
|
+
end
|
1612
1675
|
|
1613
|
-
|
1614
|
-
user_groups_cache = check_groups(new_users.map(&:to_hash).flat_map { |u| u[:user_group] || [] }.uniq, options[:user_groups_cache], options)
|
1676
|
+
whitelisted_new_users, whitelisted_users = whitelist_users(new_users.map(&:to_hash), users_list, options[:whitelists])
|
1615
1677
|
|
1616
1678
|
# conform the role on list of new users so we can diff them with the users coming from the project
|
1617
1679
|
diffable_new_with_default_role = whitelisted_new_users.map do |u|
|
@@ -1758,7 +1820,20 @@ module GoodData
|
|
1758
1820
|
def check_groups(specified_groups, user_groups_cache = nil, options = {})
|
1759
1821
|
current_user_groups = user_groups if user_groups_cache.nil? || user_groups_cache.empty?
|
1760
1822
|
groups = current_user_groups.map(&:name)
|
1761
|
-
missing_groups =
|
1823
|
+
missing_groups = []
|
1824
|
+
change_groups = {}
|
1825
|
+
specified_groups.each do |group|
|
1826
|
+
found_group = groups.find { |name| name.casecmp(group).zero? }
|
1827
|
+
if found_group.nil?
|
1828
|
+
missing_groups << group
|
1829
|
+
else
|
1830
|
+
# Change groups when they have similar group name with difference of case sensitivity
|
1831
|
+
if found_group != group
|
1832
|
+
change_groups[group] = found_group
|
1833
|
+
GoodData.logger.warn("Group with name #{group} is existed in project with name #{found_group}.")
|
1834
|
+
end
|
1835
|
+
end
|
1836
|
+
end
|
1762
1837
|
if options[:create_non_existing_user_groups]
|
1763
1838
|
missing_groups.each do |g|
|
1764
1839
|
GoodData.logger.info("Creating group #{g}")
|
@@ -1771,7 +1846,7 @@ module GoodData
|
|
1771
1846
|
"#{groups.join(',')} and you asked for #{missing_groups.join(',')}"
|
1772
1847
|
end
|
1773
1848
|
end
|
1774
|
-
current_user_groups
|
1849
|
+
[current_user_groups, change_groups]
|
1775
1850
|
end
|
1776
1851
|
|
1777
1852
|
# Update user
|
@@ -1902,6 +1977,20 @@ module GoodData
|
|
1902
1977
|
[user, roles]
|
1903
1978
|
end
|
1904
1979
|
|
1980
|
+
def upgrade_custom_v2(message, options = {})
|
1981
|
+
uri = "/gdc/md/#{pid}/datedimension/upgrade"
|
1982
|
+
poll_result = client&.post(uri, message)
|
1983
|
+
|
1984
|
+
return poll_result['wTaskStatus']['status'] if poll_result['wTaskStatus'] && poll_result['wTaskStatus']['status']
|
1985
|
+
|
1986
|
+
polling_uri = poll_result['asyncTask']['link']['poll']
|
1987
|
+
result = client&.poll_on_response(polling_uri, options) do |body|
|
1988
|
+
body && body['wTaskStatus'] && body['wTaskStatus']['status'] == 'RUNNING'
|
1989
|
+
end
|
1990
|
+
|
1991
|
+
result['wTaskStatus']['status'] == 'OK' ? 'OK' : 'FAIL'
|
1992
|
+
end
|
1993
|
+
|
1905
1994
|
def add
|
1906
1995
|
@add ||= GoodData::AutomatedDataDistribution.new(self)
|
1907
1996
|
@add
|
@@ -47,6 +47,8 @@ module GoodData
|
|
47
47
|
unless response
|
48
48
|
maql_diff_params = [:includeGrain]
|
49
49
|
maql_diff_params << :excludeFactRule if opts[:exclude_fact_rule]
|
50
|
+
maql_diff_params << :includeDeprecated if opts[:include_deprecated]
|
51
|
+
|
50
52
|
maql_diff_time = Benchmark.realtime do
|
51
53
|
response = project.maql_diff(blueprint: bp, params: maql_diff_params)
|
52
54
|
end
|
data/lib/gooddata/rest/client.rb
CHANGED
@@ -188,11 +188,11 @@ module GoodData
|
|
188
188
|
end
|
189
189
|
end
|
190
190
|
|
191
|
-
def projects(id = :all, limit = nil)
|
191
|
+
def projects(id = :all, limit = nil, offset = nil)
|
192
192
|
if limit.nil?
|
193
193
|
GoodData::Project[id, client: self]
|
194
194
|
else
|
195
|
-
GoodData::Project.all({ client: self }, limit)
|
195
|
+
GoodData::Project.all({ client: self }, limit, offset)
|
196
196
|
end
|
197
197
|
end
|
198
198
|
|
@@ -30,6 +30,7 @@ module GoodData
|
|
30
30
|
LOGIN_PATH = '/gdc/account/login'
|
31
31
|
TOKEN_PATH = '/gdc/account/token'
|
32
32
|
KEYS_TO_SCRUB = [:password, :verifyPassword, :authorizationToken]
|
33
|
+
API_LEVEL = 2
|
33
34
|
|
34
35
|
ID_LENGTH = 16
|
35
36
|
|
@@ -307,7 +308,6 @@ module GoodData
|
|
307
308
|
# Remove when TT sent in headers. Currently we need to parse from body
|
308
309
|
merge_headers!(:x_gdc_authtt => GoodData::Helpers.get_path(response, %w(userToken token)))
|
309
310
|
rescue Exception => e # rubocop:disable RescueException
|
310
|
-
GoodData.logger.error(e.message)
|
311
311
|
raise e
|
312
312
|
end
|
313
313
|
end
|
@@ -350,6 +350,9 @@ module GoodData
|
|
350
350
|
profile method.to_s.upcase, uri, request_id, stats_on do
|
351
351
|
b = proc do
|
352
352
|
params = fresh_request_params(request_id).merge(options)
|
353
|
+
|
354
|
+
params['X-GDC-VERSION'] = API_LEVEL
|
355
|
+
|
353
356
|
case method
|
354
357
|
when :get
|
355
358
|
@server[uri].get(params, &user_block)
|
@@ -514,8 +517,7 @@ module GoodData
|
|
514
517
|
begin
|
515
518
|
request.execute
|
516
519
|
rescue => e
|
517
|
-
|
518
|
-
raise e
|
520
|
+
raise "Error when uploading file #{filename}. Error: #{e}"
|
519
521
|
end
|
520
522
|
end
|
521
523
|
|
@@ -1,51 +1,51 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
1
|
+
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
|