cartodb-4.42/lib/carto/tracking/events.rb
2024-04-06 05:25:13 +00:00

519 lines
14 KiB
Ruby

require 'carto/tracking/services/pubsub_tracker'
require_dependency 'carto/tracking/formats/internal'
require_dependency 'carto/tracking/services/pubsub'
require_dependency 'carto/tracking/services/segment'
require_dependency 'carto/tracking/services/hubspot'
require_dependency 'carto/tracking/validators/visualization'
require_dependency 'carto/tracking/validators/layer'
require_dependency 'carto/tracking/validators/user'
require_dependency 'carto/tracking/validators/widget'
# IMPORTANT: Events must be kept in sync with frontend!
# See `/lib/assets/javascripts/builder/components/metrics/metrics-types.js`
DEFAULT_EVENT_VERSION = 1
module Carto
module Tracking
module Events
class Event
include ::LoggerHelper
def initialize(reporter_id, properties)
properties.merge!({event_version: event_version})
@format = Carto::Tracking::Formats::Internal.new(properties)
@reporter = Carto::User.find(reporter_id)
end
def name
self.class.name.demodulize.underscore.humanize.capitalize
end
def event_version
DEFAULT_EVENT_VERSION
end
def self.required_properties(*required_properties)
@required_properties ||= []
@required_properties += required_properties
end
def required_properties
these_required_properties = self.class.instance_eval { @required_properties || [] }
these_required_properties + self.class.superclass.required_properties
end
def self.optional_properties(*optional_properties)
@optional_properties ||= []
@optional_properties += optional_properties
@optional_properties += [:event_version]
end
def optional_properties
these_optional_properties = self.class.instance_eval { @optional_properties || [] }
these_optional_properties + self.class.superclass.optional_properties
end
def report
report!
rescue StandardError => e
log_error(
message: 'Carto::Tracking: Error reporting event',
exception: e,
event: @format.to_hash.merge(name: name)
)
end
def report!
check_required_properties!
check_no_extra_properties!
authorize!
report_to_methods = methods.select do |method_name|
method_name.to_s.start_with?('report_to')
end
report_to_methods.each do |report_method|
send(report_method)
end
end
private
def check_required_properties!
missing_properties = required_properties - @format.to_hash.symbolize_keys.keys
unless missing_properties.empty?
message = "#{name} is missing the following properties: #{missing_properties.join(', ')}"
raise Carto::UnprocesableEntityError.new(message)
end
end
def check_no_extra_properties!
extra_properties = @format.to_hash.symbolize_keys.keys - required_properties - optional_properties
unless extra_properties.empty?
message = "#{name} is adding the following extra properties: #{extra_properties.join(', ')}"
raise Carto::UnprocesableEntityError.new(message)
end
end
# Validators are modules that should be included in Event classes. These
# modules contain methods that start with 'check_'. They raise an
# exception if whatever condition they validate is not met. All methods
# that match this criteria in validator modules included in an Event
# class will be run automatically when .report or .report! is called for
# that same class.
def authorize!
check_methods = methods.select do |method_name|
method_name.to_s.start_with?('check_')
end
check_methods.each do |check_method|
send(check_method)
end
end
end
class ExportedMap < Event
include Carto::Tracking::Services::Hubspot
include Carto::Tracking::Services::Segment
include Carto::Tracking::Services::PubSub
include Carto::Tracking::Validators::Visualization::Readable
include Carto::Tracking::Validators::User
required_properties :user_id, :visualization_id
def pubsub_name
'map_exported'
end
def event_version
2
end
end
class MapEvent < Event
include Carto::Tracking::Services::Segment
include Carto::Tracking::Services::PubSub
include Carto::Tracking::Validators::Visualization::Writable
include Carto::Tracking::Validators::User
required_properties :user_id, :visualization_id
end
class CreatedMap < MapEvent
include Carto::Tracking::Services::Hubspot
required_properties :origin
optional_properties :connection
def pubsub_name
'map_created'
end
def event_version
3
end
end
class DeletedMap < MapEvent
def pubsub_name
'map_deleted'
end
def event_version
2
end
end
class ModifiedMap < MapEvent
def pubsub_name
'map_updated'
end
def event_version
2
end
end
class PublishedMap < Event
include Carto::Tracking::Services::Hubspot
include Carto::Tracking::Services::Segment
include Carto::Tracking::Services::PubSub
include Carto::Tracking::Validators::Visualization::Writable
include Carto::Tracking::Validators::User
required_properties :user_id, :visualization_id
def pubsub_name
'map_published'
end
def event_version
2
end
end
class ConnectionEvent < Event
include Carto::Tracking::Services::Hubspot
include Carto::Tracking::Services::Segment
include Carto::Tracking::Validators::User
required_properties :user_id, :connection
end
class CompletedConnection < ConnectionEvent; end
class FailedConnection < ConnectionEvent
include Carto::Tracking::Services::PubSub
def pubsub_name
'import_failed'
end
def event_version
3
end
end
class FailedSync < Event
include Carto::Tracking::Services::PubSub
required_properties :user_id, :connection
def pubsub_name
'sync_failed'
end
end
class ExceededQuota < Event
include Carto::Tracking::Services::Segment
include Carto::Tracking::Services::PubSub
include Carto::Tracking::Validators::User
required_properties :user_id
optional_properties :quota_overage
def pubsub_name
'quota_exceeded'
end
end
class ScoredTrendingMap < Event
include Carto::Tracking::Services::Segment
include Carto::Tracking::Validators::Visualization::Writable
include Carto::Tracking::Validators::User
required_properties :user_id, :visualization_id, :mapviews
end
class VisitedPrivatePage < Event
include Carto::Tracking::Services::Segment
include Carto::Tracking::Validators::User
required_properties :user_id, :page
def report_to_user_model
@format.fetch_record!(:user).view_dashboard if @format.to_hash['page'] == 'dashboard'
end
end
class DatasetEvent < Event
include Carto::Tracking::Services::Segment
include Carto::Tracking::Services::PubSub
include Carto::Tracking::Validators::Visualization::Writable
include Carto::Tracking::Validators::User
required_properties :user_id, :visualization_id
end
class CreatedDataset < DatasetEvent
include Carto::Tracking::Services::Hubspot
required_properties :origin
optional_properties :connection
def pubsub_name
'dataset_created'
end
def event_version
3
end
end
class DeletedDataset < DatasetEvent
def pubsub_name
'dataset_deleted'
end
def event_version
2
end
end
class AnalysisEvent < Event
include Carto::Tracking::Services::Hubspot
include Carto::Tracking::Services::Segment
include Carto::Tracking::Services::PubSub
include Carto::Tracking::Validators::Visualization::Writable
include Carto::Tracking::Validators::User
required_properties :user_id, :visualization_id, :analysis
end
class CreatedAnalysis < AnalysisEvent
def pubsub_name
'analysis_created'
end
end
class ModifiedAnalysis < AnalysisEvent
def pubsub_name
'analysis_updated'
end
end
class DeletedAnalysis < AnalysisEvent
def pubsub_name
'analysis_deleted'
end
end
class AppliedSql < Event
include Carto::Tracking::Services::Hubspot
include Carto::Tracking::Validators::Visualization::Writable
include Carto::Tracking::Validators::User
required_properties :user_id, :visualization_id, :sql
optional_properties :node_id, :dataset_id
end
class AppliedCartocss < Event
include Carto::Tracking::Services::Hubspot
include Carto::Tracking::Validators::Visualization::Writable
include Carto::Tracking::Validators::User
required_properties :user_id, :visualization_id, :layer_id, :cartocss
end
class ModifiedStyleForm < AppliedCartocss
required_properties :style_properties
end
class CreatedWidget < Event
include Carto::Tracking::Services::Segment
include Carto::Tracking::Validators::Widget::Existence
include Carto::Tracking::Validators::Visualization::Writable
include Carto::Tracking::Validators::User
required_properties :user_id, :visualization_id, :widget_id
end
class DownloadedLayer < Event
include Carto::Tracking::Services::Segment
include Carto::Tracking::Validators::Visualization::Writable
include Carto::Tracking::Validators::Layer
include Carto::Tracking::Validators::User
required_properties :user_id, :visualization_id, :layer_id, :format,
:source, :visible, :table_name
optional_properties :from_view
end
class StyledByValue < Event
include Carto::Tracking::Services::Segment
include Carto::Tracking::Validators::Visualization::Writable
include Carto::Tracking::Validators::User
required_properties :user_id, :visualization_id, :attribute, :attribute_type
end
class DraggedNode < Event
include Carto::Tracking::Services::Segment
include Carto::Tracking::Validators::Visualization::Writable
include Carto::Tracking::Validators::User
required_properties :user_id, :visualization_id
end
class CreatedLayer < Event
include Carto::Tracking::Services::Segment
include Carto::Tracking::Validators::Visualization::Writable
include Carto::Tracking::Validators::Layer
include Carto::Tracking::Validators::User
required_properties :user_id, :visualization_id, :layer_id, :empty
end
class ChangedDefaultGeometry < Event
include Carto::Tracking::Services::Segment
include Carto::Tracking::Validators::Visualization::Writable
include Carto::Tracking::Validators::User
required_properties :user_id, :visualization_id
end
class AggregatedGeometries < Event
include Carto::Tracking::Services::Segment
include Carto::Tracking::Validators::Visualization::Writable
include Carto::Tracking::Validators::User
required_properties :user_id, :visualization_id, :previous_agg_type, :agg_type
end
class UsedAdvancedMode < Event
include Carto::Tracking::Services::Segment
include Carto::Tracking::Validators::Visualization::Writable
include Carto::Tracking::Validators::User
required_properties :user_id, :visualization_id, :mode_type
end
class OauthAppEvent < Event
include Carto::Tracking::Services::Segment
include Carto::Tracking::Services::PubSub
include Carto::Tracking::Validators::User
required_properties :user_id, :app_id, :app_name
end
class CreatedOauthApp < OauthAppEvent
def pubsub_name
'oauth_app_created'
end
end
class DeletedOauthApp < OauthAppEvent
def pubsub_name
'oauth_app_deleted'
end
end
class CreatedOauthAppUser < OauthAppEvent
def pubsub_name
'oauth_app_user_created'
end
end
class DeletedOauthAppUser < OauthAppEvent
def pubsub_name
'oauth_app_user_deleted'
end
end
class UpdatedFeatureFlag < Event
include Carto::Tracking::Services::PubSub
required_properties :user_id, :feature_flag
def pubsub_name
'feature_flag_updated'
end
end
# Models a generic event for segment.
class SegmentEvent < Event
include Carto::Tracking::Services::Segment
attr_reader :name
private_class_method :new
# Just pass any hash at `properties` and it will be sent to Segment.
def self.build(name, reporter_id, properties)
new(name, reporter_id, properties) if EVENTS.include?(name)
end
private
EVENTS = ['WebGL stats'].freeze
def initialize(name, reporter_id, properties)
@name = name
@properties = properties
@format = SegmentFormat.new(@properties)
@reporter = Carto::User.where(id: reporter_id).first
end
end
class SegmentFormat < Carto::Tracking::Formats::Internal
def to_segment
data = super
data[:data_properties] = to_hash
data
end
end
end
end
end