199 lines
5.8 KiB
Ruby
199 lines
5.8 KiB
Ruby
|
require_relative 'template'
|
||
|
require_relative '../http/client'
|
||
|
require_dependency 'carto/named_maps/template'
|
||
|
|
||
|
module Carto
|
||
|
module NamedMaps
|
||
|
class Api
|
||
|
|
||
|
include ::LoggerHelper
|
||
|
|
||
|
HTTP_CONNECT_TIMEOUT_SECONDS = 45
|
||
|
HTTP_REQUEST_TIMEOUT_SECONDS = 60
|
||
|
RETRY_TIME_SECONDS = 2
|
||
|
MAX_RETRY_ATTEMPTS = 3
|
||
|
|
||
|
attr_accessor :user, :visualization
|
||
|
|
||
|
def initialize(visualization)
|
||
|
@visualization = visualization
|
||
|
@user = visualization.user
|
||
|
@named_map_template = Carto::NamedMaps::Template.new(visualization)
|
||
|
end
|
||
|
|
||
|
def index
|
||
|
stats_aggregator.timing('carto-named-maps-api.index') do
|
||
|
response = stats_aggregator.timing('call') do
|
||
|
http_client.get(url, request_params)
|
||
|
end
|
||
|
|
||
|
if response.code.to_s =~ /^2/
|
||
|
::JSON.parse(response.response_body).deep_symbolize_keys
|
||
|
else
|
||
|
log_response(response, 'index')
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
def create
|
||
|
stats_aggregator.timing('carto-named-maps-api.create') do
|
||
|
params = request_params
|
||
|
params[:body] = @named_map_template.to_json
|
||
|
|
||
|
response = http_client.post(url, params)
|
||
|
|
||
|
response_code = response.code
|
||
|
if response_code.to_s =~ /^2/
|
||
|
::JSON.parse(response.response_body).deep_symbolize_keys
|
||
|
elsif response_code != 409 # Ignore when max number of templates is reached
|
||
|
log_response(response, 'create')
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
def show
|
||
|
stats_aggregator.timing('carto-named-maps-api.show') do
|
||
|
response = stats_aggregator.timing('call') do
|
||
|
url = url(template_name: @named_map_template.name)
|
||
|
|
||
|
response = http_client.get(url, request_params)
|
||
|
end
|
||
|
|
||
|
response_code = response.code
|
||
|
if response_code.to_s =~ /^2/
|
||
|
::JSON.parse(response.response_body).deep_symbolize_keys
|
||
|
else
|
||
|
log_response(response, 'show') unless response_code == 404
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
def update(retries: 0)
|
||
|
stats_aggregator.timing('carto-named-maps-api.update') do
|
||
|
params = request_params
|
||
|
params[:body] = @named_map_template.to_json
|
||
|
|
||
|
response = http_client.put(url(template_name: @named_map_template.name), params)
|
||
|
|
||
|
response_code_string = response.code.to_s
|
||
|
if response_code_string =~ /^2/
|
||
|
::JSON.parse(response.response_body).deep_symbolize_keys
|
||
|
elsif response_code_string =~ /^5/ && retries < MAX_RETRY_ATTEMPTS
|
||
|
sleep(RETRY_TIME_SECONDS**retries)
|
||
|
update(retries: retries + 1)
|
||
|
else
|
||
|
log_response(response, 'update')
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
def destroy
|
||
|
stats_aggregator.timing('carto-named-maps-api.destroy') do
|
||
|
url = url(template_name: @named_map_template.name)
|
||
|
|
||
|
response = http_client.delete(url, request_params)
|
||
|
|
||
|
response_code = response.code
|
||
|
if response_code.to_s =~ /^2/
|
||
|
response.response_body
|
||
|
else
|
||
|
log_response(response, 'destroy') unless response_code == 404
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
def upsert
|
||
|
show ? update : create
|
||
|
end
|
||
|
|
||
|
private
|
||
|
|
||
|
def stats_aggregator
|
||
|
@@stats_aggregator_instance ||= CartoDB::Stats::EditorAPIs.instance
|
||
|
end
|
||
|
|
||
|
def url(template_name: '')
|
||
|
username = user.username
|
||
|
user_url = CartoDB.subdomainless_urls? ? "/user/#{username}" : ''
|
||
|
|
||
|
"#{protocol}://#{host(username)}:#{port}#{user_url}/api/v1/map/named/#{template_name}?api_key=#{user.api_key}"
|
||
|
end
|
||
|
|
||
|
def request_params
|
||
|
{
|
||
|
headers: headers(user.username),
|
||
|
ssl_verifypeer: ssl_verifypeer,
|
||
|
ssl_verifyhost: ssl_verifyhost,
|
||
|
followlocation: true,
|
||
|
connecttimeout: HTTP_CONNECT_TIMEOUT_SECONDS,
|
||
|
timeout: HTTP_REQUEST_TIMEOUT_SECONDS
|
||
|
}
|
||
|
end
|
||
|
|
||
|
def headers(username)
|
||
|
@headers ||= {
|
||
|
'content-type': 'application/json',
|
||
|
'host': domain(username)
|
||
|
}
|
||
|
end
|
||
|
|
||
|
def domain(username)
|
||
|
return @domain if @domain
|
||
|
|
||
|
config_domain = Cartodb.get_config(:tiler, 'internal', 'domain')
|
||
|
|
||
|
CartoDB.subdomainless_urls? ? config_domain : "#{username}.#{config_domain}"
|
||
|
end
|
||
|
|
||
|
def port
|
||
|
@port ||= Cartodb.get_config(:tiler, 'internal', 'port')
|
||
|
end
|
||
|
|
||
|
def protocol
|
||
|
@protocol ||= Cartodb.get_config(:tiler, 'internal', 'protocol')
|
||
|
end
|
||
|
|
||
|
def ssl_verifypeer
|
||
|
@verifycert ||= Cartodb.get_config(:tiler, 'internal', 'verifycert')
|
||
|
end
|
||
|
|
||
|
def ssl_verifyhost
|
||
|
ssl_verifypeer ? 2 : 0
|
||
|
end
|
||
|
|
||
|
def host(username)
|
||
|
return @host if @host
|
||
|
|
||
|
config_host = Cartodb.get_config(:tiler, 'internal', 'host')
|
||
|
|
||
|
@host ||= config_host.blank? ? domain(username) : config_host
|
||
|
end
|
||
|
|
||
|
def http_client
|
||
|
@http_client ||= Carto::Http::Client.get('named_maps')
|
||
|
end
|
||
|
|
||
|
def log_response(response, action)
|
||
|
log_error(
|
||
|
message: 'Error in named maps API',
|
||
|
current_user: user,
|
||
|
visualization_id: visualization.id,
|
||
|
action: action,
|
||
|
request_url: response.request.url,
|
||
|
status: response.code,
|
||
|
response_body: response.body
|
||
|
)
|
||
|
rescue Encoding::UndefinedConversionError => e
|
||
|
# Hotfix for preventing https://rollbar.com/carto/CartoDB/items/41457 until we find the root cause
|
||
|
# https://cartoteam.slack.com/archives/CEQLWTW9Z/p1599134417001900
|
||
|
# https://app.clubhouse.io/cartoteam/story/101908/fix-encoding-error-while-logging-request
|
||
|
Rollbar.error(e)
|
||
|
end
|
||
|
|
||
|
def log_context
|
||
|
super.merge(request_id: Carto::CurrentRequest.request_id, component: 'cartodb.named-maps-client')
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|