cartodb-4.42/app/services/carto/redis_export_service.rb
2024-04-06 05:25:13 +00:00

158 lines
4.4 KiB
Ruby

require 'json'
# Version History
# 1.0.0: export organization metadata
module Carto
module RedisExportServiceConfiguration
CURRENT_VERSION = '1.0.0'.freeze
def compatible_version?(version)
version.to_i == CURRENT_VERSION.split('.')[0].to_i
end
end
module RedisExportServiceImporter
include RedisExportServiceConfiguration
def restore_redis_from_json_export(exported_json_string)
restore_redis_from_hash_export(JSON.parse(exported_json_string).deep_symbolize_keys)
end
def remove_redis_from_json_export(exported_json_string)
remove_redis_from_hash_export(JSON.parse(exported_json_string).deep_symbolize_keys)
end
def restore_redis_from_hash_export(exported_hash)
raise 'Wrong export version' unless compatible_version?(exported_hash[:version])
restore_redis(exported_hash[:redis])
end
def remove_redis_from_hash_export(exported_hash)
raise 'Wrong export version' unless compatible_version?(exported_hash[:version])
remove_redis(exported_hash[:redis])
end
private
def restore_redis(redis_export)
restore_keys($users_metadata, redis_export[:users_metadata])
restore_named_maps($tables_metadata, redis_export[:tables_metadata])
end
def remove_redis(redis_export)
remove_keys($users_metadata, redis_export[:users_metadata])
remove_keys($tables_metadata, redis_export[:tables_metadata])
end
def restore_keys(redis_db, redis_keys)
redis_keys.each do |key, value|
redis_db.restore(key, value[:ttl], Base64.decode64(value[:value]))
end
end
def restore_named_maps(redis_db, redis_keys)
redis_keys.select { |k| k =~ /map_tpl\|/ }.each do |key, value|
value.each do |name, named_map_config|
redis_db.hset(key, name, Base64.decode64(named_map_config))
end
end
end
def remove_keys(redis_db, redis_keys)
redis_keys.each do |key|
redis_db.del(key)
end
end
end
module RedisExportServiceExporter
include RedisExportServiceConfiguration
def export_organization_json_string(organization)
export_organization_json_hash(organization).to_json
end
def export_organization_json_hash(organization)
{
version: CURRENT_VERSION,
redis: export_organization(organization)
}
end
def export_user_json_string(user)
export_user_json_hash(user).to_json
end
def export_user_json_hash(user)
{
version: CURRENT_VERSION,
redis: export_user(user)
}
end
private
def export_organization(organization)
{
users_metadata: export_dataservices("org:#{organization.name}"),
tables_metadata: {}
}
end
def export_user(user)
{
users_metadata: export_dataservices("user:#{user.username}"),
tables_metadata: export_named_maps(user)
}
end
def export_dataservices(prefix)
$users_metadata_secondary.keys("#{prefix}:*").map { |key|
export_key($users_metadata_secondary, key)
}.reduce({}, &:merge)
end
def export_named_maps(user)
named_maps_key = "map_tpl|#{user.username}"
named_maps_keys = $tables_metadata_secondary.hkeys(named_maps_key).reject do |named_map|
matches_user_visualization?(named_map, user)
end
named_maps_hash = named_maps_keys.reduce({}) do |m, named_map|
m.merge(named_map => Base64.encode64($tables_metadata_secondary.hget(named_maps_key, named_map)))
end
return {} unless named_maps_hash.any?
{ named_maps_key => named_maps_hash }
end
def matches_user_visualization?(named_map, user)
re = /tpl_(?<viz_id>.+)/
match = re.match(named_map)
match && visualization_exists?(id: match[:viz_id].tr('_', '-'), user_id: user.id)
end
def visualization_exists?(criteria)
Carto::Visualization.where(criteria).exists?
rescue StandardError
false
end
def export_key(redis_db, key)
{
key => {
ttl: [0, redis_db.pttl(key)].max, # PTTL returns -1 if not set, clamp to 0
value: Base64.encode64(redis_db.dump(key))
}
}
end
end
# Both String and Hash versions are provided because `deep_symbolize_keys` won't symbolize through arrays
# and having separated methods make handling and testing much easier.
class RedisExportService
include RedisExportServiceImporter
include RedisExportServiceExporter
end
end