landable 1.12.3 → 1.13.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8ac7e3da514137a5a1c613fba88a310902a9588a
4
- data.tar.gz: cb26053550dec33f9165ef7f708f4d925df0c9df
3
+ metadata.gz: 79b91e68096271b06e44f3660fe1a577b2106829
4
+ data.tar.gz: d2fc437c485007597bcbbbbafc9e525e5eda8290
5
5
  SHA512:
6
- metadata.gz: e7a9ee77e262b28ee572e6b7e58302cb1766bafc2d9811040bdfe618e439b64ad22a786a3d7a753989aa434cd159f5ee214fefb31e381ccdfd97c882a4027e55
7
- data.tar.gz: c31d2968ff8c11ca4d8ddab2d9a249edf84a9043eb4b25e0052adee1c14d8964c06c6bb6dfa6a30e2e3aafd34cc1a4b0ef48889288965203dcc27dba4f4e8d7f
6
+ metadata.gz: e3c967e86fec973e57c27c7ba24702cb9ff910f6b948c19660ec9f6ca3ffe56d846ca8c1b6bb3071e7661424d17eb5856902aab186b019f49bf94c0ad6fb5c79
7
+ data.tar.gz: ffbae9ae3d1d86064e7820f80ca173c574b5bf867523fc1a95fe41a6c6c35c34067554387bdc4e5b18764c7298b552de0925fa6113d1bee3d4674aa4ff1c7164
@@ -2,7 +2,11 @@
2
2
 
3
3
  See README.md before updating this file.
4
4
 
5
- ## Unreleased [#](https://github.com/enova/landable/compare/v1.12.1...master)
5
+ ## Unreleased [#](https://github.com/enova/landable/compare/v1.13.0...v1.13.1)
6
+
7
+ ## 1.13.1 [#](https://github.com/enova/landable/compare/v1.12.3...v1.13.1)
8
+ * BugFix: Default referer.path to '' [#60]
9
+ * Feature: Advanced Message Queuing Protocol to Publish Events [#58]
6
10
 
7
11
  ## 1.12.3 [#](https://github.com/enova/landable/compare/v1.12.2...v1.12.3)
8
12
  * Feature: Add Traffic Object Helper [#50]
@@ -51,6 +51,28 @@ Landable.configure do |config|
51
51
 
52
52
  # If you want to save a different UserAgent if the request.user_agent is blank, set it here
53
53
  # config.blank_user_agent_string = 'blank'
54
+
55
+ # Uncomment to enable publishing of events to an amqp messaging service.
56
+ #
57
+ # This feature requires the mounting application to configure a messenger
58
+ # class which includes a publish method. The publish method should
59
+ # take the message as an input, cast it appropriately
60
+ # then deliver the message to whatever amqp service you are running.
61
+ # the EventPublisher class will only attempt to send a message if
62
+ # enabled = true and messaging_service is set.
63
+ # The configuration params are a collection, like so:
64
+ # config.amqp_configuration = {
65
+ # site_segment: 'mybrand:myproduct:myapp',
66
+ # messaging_service: AmqpMessagingService,
67
+ # enabled: 'true',
68
+ # event_mapping: {
69
+ # '/' => 'Home page',
70
+ # '/my_path' => { 'GET' => 'Customer Landed',
71
+ # 'POST' => 'Customer Submitted',
72
+ # 'DELETE' => 'Customer Left'
73
+ # }
74
+ # }.freeze
75
+ # }
54
76
  end
55
77
 
56
78
  # Configure asset uploads. Assets will be uploaded to public/uploads by default.
@@ -1,6 +1,6 @@
1
1
  module Landable
2
2
  class Configuration
3
- attr_accessor :api_url, :public_url
3
+ attr_accessor :api_url, :public_url, :amqp_configuration
4
4
  attr_writer :api_namespace, :public_namespace
5
5
  attr_writer :api_host, :public_host
6
6
  attr_writer :categories
@@ -10,7 +10,12 @@ module Landable
10
10
  attr_writer :reserved_paths, :partials_to_templates, :database_schema_prefix
11
11
  attr_writer :publicist_url, :audit_flags
12
12
  attr_writer :blank_user_agent_string, :untracked_paths
13
- attr_writer :dnt_enabled
13
+ attr_writer :dnt_enabled, :amqp_event_mapping, :amqp_site_segment
14
+ attr_writer :amqp_service_enabled, :amqp_messaging_service
15
+
16
+ def amqp_configuration
17
+ @amqp_configuration ||= {}
18
+ end
14
19
 
15
20
  def authenticators
16
21
  @authenticators || raise("No Landable authenticator configured.")
@@ -152,10 +157,21 @@ module Landable
152
157
 
153
158
  def dnt_enabled
154
159
  return true if @dnt_enabled.nil?
155
-
156
160
  @dnt_enabled
157
161
  end
158
162
 
163
+ def amqp_site_segment
164
+ @amqp_site_segment ||= Rails.application.class.parent_name
165
+ end
166
+
167
+ def amqp_event_mapping
168
+ @amqp_event_mapping ||= {}
169
+ end
170
+
171
+ def amqp_service_enabled
172
+ amqp_configuration[:enabled] && amqp_configuration[:messaging_service]
173
+ end
174
+
159
175
 
160
176
  class Screenshots
161
177
  attr_accessor :autorun
@@ -5,6 +5,7 @@ require 'landable/traffic/scan_tracker'
5
5
  require 'landable/traffic/scrape_tracker'
6
6
  require 'landable/traffic/user_tracker'
7
7
  require 'landable/traffic/noop_tracker'
8
+ require 'landable/traffic/event_publisher'
8
9
 
9
10
  module Landable
10
11
  module Traffic
@@ -0,0 +1,115 @@
1
+ module Landable
2
+ module EventPublisher
3
+ mattr_reader :event_type, :page_view, :visit
4
+
5
+ class << self
6
+ def publish(page_view)
7
+ @configuration = Landable.configuration.amqp_configuration
8
+ return unless enabled?
9
+
10
+ @event_type = event_type(page_view)
11
+ return unless @event_type
12
+
13
+ @page_view = page_view
14
+ @visit = page_view.visit
15
+ messaging_service.publish(message)
16
+ end
17
+
18
+ private
19
+
20
+ def enabled?
21
+ @enabled ||= @configuration[:enabled] && messaging_service.present?
22
+ end
23
+
24
+ def messaging_service
25
+ @messaging_service ||= @configuration[:messaging_service]
26
+ end
27
+
28
+ def event_mapping
29
+ @event_mapping ||= @configuration[:event_mapping]
30
+ end
31
+
32
+ def event_type(page_view)
33
+ event_type = event_mapping[page_view.path]
34
+
35
+ if event_type.is_a?(Hash)
36
+ request_type = page_view.http_method
37
+ event_type = event_type[request_type]
38
+ end
39
+ event_type
40
+ end
41
+
42
+ def site_segment
43
+ @site_segment ||= @configuration[:site_segment]
44
+ end
45
+
46
+ def message
47
+ visit = @visit
48
+ attribution = visit.try(:attribution)
49
+ referer = visit.try(:referer)
50
+ visitor = visit.visitor
51
+ page_view = @page_view
52
+ user_agent = visitor.try(:raw_user_agent)
53
+ user_agent_type = user_agent.try(:raw_user_agent_type)
54
+ event_type = @event_type
55
+ {
56
+ site_segment: site_segment,
57
+ visit_id: visit.id,
58
+ event: event_type.to_s,
59
+ page_view_id: page_view.page_view_id,
60
+ request_type: page_view.http_method,
61
+ created_at: page_view.created_at,
62
+ cookie_id: visit.cookie_id,
63
+ owner_id: visit.try(:owner_id),
64
+ owner: visit.try(:owner).try(:owner),
65
+ referer_id: referer.try(:id),
66
+ domain_id: referer.try(:domain_id),
67
+ domain: referer.try(:domain),
68
+ ip_address_id: visitor.try(:ip_address_id),
69
+ ip_address: visitor.try(:ip_address).try(:to_s),
70
+ user_agent_id: user_agent.try(:id),
71
+ user_agent: user_agent.try(:user_agent),
72
+ user_agent_type_id: user_agent_type.try(:id),
73
+ user_agent_type: user_agent_type.try(:user_agent_type),
74
+ attribution_id: attribution.try(:id),
75
+ ad_group_id: attribution.try(:ad_group_id),
76
+ ad_group: attribution.try(:ad_group),
77
+ ad_type_id: attribution.try(:ad_type_id),
78
+ ad_type: attribution.try(:ad_type),
79
+ bid_match_type_id: attribution.try(:bid_match_type_id),
80
+ bid_match_type: attribution.try(:bid_match_type),
81
+ campaign_id: attribution.try(:campaign_id),
82
+ campaign: attribution.try(:campaign),
83
+ content_id: attribution.try(:content_id),
84
+ content: attribution.try(:content),
85
+ creative_id: attribution.try(:creative_id),
86
+ creative: attribution.try(:creative),
87
+ device_type_id: attribution.try(:device_type_id),
88
+ device_type: attribution.try(:device_type),
89
+ experiment_id: attribution.try(:experiment_id),
90
+ experiment: attribution.try(:experiment),
91
+ keyword_id: attribution.try(:keyword_id),
92
+ keyword: attribution.try(:keyword),
93
+ match_type_id: attribution.try(:match_type_id),
94
+ match_type: attribution.try(:match_type),
95
+ medium_id: attribution.try(:medium_id),
96
+ medium: attribution.try(:medium),
97
+ network_id: attribution.try(:network_id),
98
+ network: attribution.try(:network),
99
+ placement_id: attribution.try(:placement_id),
100
+ placement: attribution.try(:placement),
101
+ position_id: attribution.try(:position_id),
102
+ position: attribution.try(:position),
103
+ search_term_id: attribution.try(:search_term_id),
104
+ search_term: attribution.try(:search_term),
105
+ source_id: attribution.try(:source_id),
106
+ source: attribution.try(:source),
107
+ target_id: attribution.try(:target_id),
108
+ target: attribution.try(:target),
109
+ path_id: page_view.path_id,
110
+ path: page_view.path
111
+ }
112
+ end
113
+ end
114
+ end
115
+ end
@@ -76,8 +76,8 @@ module Landable
76
76
  def for(controller)
77
77
  type = controller.request.user_agent.presence && Landable::Traffic::UserAgent[controller.request.user_agent].user_agent_type
78
78
  type = 'noop' if Landable.configuration.traffic_enabled == :html and not controller.request.format.html?
79
- type = 'user'if type.nil?
80
- type = 'user'if controller.request.query_parameters.slice(*TRACKING_KEYS).any?
79
+ type = 'user' if type.nil?
80
+ type = 'user' if controller.request.query_parameters.with_indifferent_access.slice(*TRACKING_KEYS).any?
81
81
 
82
82
  "Landable::Traffic::#{type.classify}Tracker".constantize.new(controller)
83
83
  end
@@ -173,7 +173,7 @@ module Landable
173
173
  query = params.except(*ATTRIBUTION_KEYS)
174
174
 
175
175
  @referer = Referer.where(domain_id: Domain[referer_uri.host],
176
- path_id: Path[referer_uri.path],
176
+ path_id: Path[referer_uri_path],
177
177
  query_string_id: QueryString[query.to_query],
178
178
  attribution_id: attribution.id).first_or_create
179
179
  end
@@ -236,6 +236,10 @@ module Landable
236
236
  @referer_uri ||= URI(URI.encode(request.referer)) if request.referer
237
237
  end
238
238
 
239
+ def referer_uri_path
240
+ referer_uri.try(:path) || ''
241
+ end
242
+
239
243
  def external_referer?
240
244
  referer_uri && referer_uri.host != request.host
241
245
  end
@@ -271,6 +275,10 @@ module Landable
271
275
  hash
272
276
  end
273
277
 
278
+ def query_parameters
279
+ @query_parameters ||= request.query_parameters.with_indifferent_access
280
+ end
281
+
274
282
  def tracking_parameters
275
283
  @tracking_parameters ||= extract_tracking(query_parameters)
276
284
  end
@@ -280,7 +288,7 @@ module Landable
280
288
  end
281
289
 
282
290
  def attribution_parameters
283
- @attribution_parameters ||= tracking_parameters.slice(*ATTRIBUTION_KEYS)
291
+ @attribution_parameters ||= tracking_parameters.with_indifferent_access.slice(*ATTRIBUTION_KEYS)
284
292
  end
285
293
 
286
294
  def attribution
@@ -33,13 +33,18 @@ module Landable
33
33
  p.http_status = response.status
34
34
 
35
35
  p.visit_id = @visit_id
36
+ # this is strange, yes, but is it better than a db call?
37
+ p.created_at = Time.current
36
38
 
37
39
  p.response_time = ( Time.now - @start_time ) * 1000
38
40
  end
39
41
  end
40
42
 
41
43
  def save
42
- record_page_view
44
+ p = record_page_view
45
+ if Landable.configuration.amqp_service_enabled
46
+ EventPublisher.publish(p)
47
+ end
43
48
 
44
49
  session[:landable] = {
45
50
  KEYS[:visit_id] => @visit_id,
@@ -1,8 +1,8 @@
1
1
  module Landable
2
2
  module VERSION
3
3
  MAJOR = 1
4
- MINOR = 12
5
- PATCH = 3
4
+ MINOR = 13
5
+ PATCH = 1
6
6
 
7
7
  STRING = [MAJOR, MINOR, PATCH].compact.join('.')
8
8
  end
@@ -26,4 +26,5 @@ Dummy::Application.configure do
26
26
  # This option may cause significant delays in view rendering with a large
27
27
  # number of complex assets.
28
28
  config.assets.debug = true
29
+ config.serve_static_files = true
29
30
  end
@@ -13,7 +13,7 @@ Dummy::Application.configure do
13
13
  config.eager_load = false
14
14
 
15
15
  # Configure static asset server for tests with Cache-Control for performance.
16
- config.serve_static_assets = true
16
+ config.serve_static_files = true
17
17
  config.static_cache_control = "public, max-age=3600"
18
18
 
19
19
  # Show full error reports and disable caching.
@@ -1,4 +1,5 @@
1
1
  require 'landable'
2
+ require Rails.root.join('lib', 'bunny_messaging_service.rb')
2
3
 
3
4
  Landable.configure do |config|
4
5
  config.api_namespace = '/api'
@@ -11,10 +12,20 @@ Landable.configure do |config|
11
12
  config.partials_to_templates = %w(partials/foobazz)
12
13
 
13
14
  config.reserved_paths = %w(/reserved_path_set_in_initializer /reject/.* /admin.*)
14
-
15
15
  config.database_schema_prefix = 'dummy'
16
-
17
16
  config.audit_flags = %w(loans apr)
17
+
18
+ config.amqp_configuration = {
19
+ site_segment: 'mybrand:myproduct:myapp',
20
+ messaging_service: BunnyMessagingService,
21
+ enabled: 'true',
22
+ event_mapping: {
23
+ '/my_path' => { 'GET' => 'Customer Landed',
24
+ 'POST' => 'Customer Submitted',
25
+ 'DELETE' => 'Customer Left'
26
+ }
27
+ }.freeze
28
+ }
18
29
  end
19
30
 
20
31
  # Configure asset uploads. Assets will be uploaded to public/uploads by default.
@@ -0,0 +1,7 @@
1
+ class BunnyMessagingService
2
+ class << self
3
+ def publish(message)
4
+ message
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,94 @@
1
+ require 'spec_helper'
2
+
3
+ module Landable
4
+ module Traffic
5
+ describe 'EventPublisher', type: :controller do
6
+
7
+ controller(ApplicationController) do
8
+ include Landable::Traffic
9
+ prepend_around_action :track_with_landable!
10
+
11
+ def my_path
12
+ render nothing: true
13
+ end
14
+ end
15
+
16
+ before do
17
+ routes.draw do
18
+ get "/my_path" => 'anonymous#my_path'
19
+ post '/my_path' => 'anonymous#my_path'
20
+ delete '/my_path' => 'anonymous#my_path'
21
+ end
22
+ end
23
+
24
+ let(:message_keys) do
25
+ [:ad_group, :ad_type, :bid_match_type, :campaign, :content, :creative, :device_type,
26
+ :experiment, :keyword, :match_type, :medium, :network, :placement, :position,
27
+ :search_term, :source, :target]
28
+ end
29
+
30
+ let(:attribution) do
31
+ message_keys.each_with_object({}) do |attribute, hash|
32
+ hash[attribute] = "test_#{attribute}"
33
+ end
34
+ end
35
+
36
+ let(:tracker) { controller.instance_variable_get(:@tracker) }
37
+ let(:page_view) { PageView.last }
38
+ let(:published_message) { EventPublisher.publish(page_view) }
39
+
40
+ it 'should properly properly set the attribution data and send it within a message' do
41
+ get :my_path, attribution
42
+ # binding.pry
43
+ message_keys.each do |attribute|
44
+ expect(published_message[attribute]).to eq("test_#{attribute}")
45
+ end
46
+ end
47
+
48
+ it 'should properly set the event types for GET requests when multiple request types use the same route' do
49
+ get :my_path, attribution
50
+ expect(published_message[:path]).to eq('/my_path')
51
+ expect(published_message[:request_type]).to eq('GET')
52
+ expect(published_message[:event]).to eq('Customer Landed')
53
+ end
54
+
55
+ it 'should properly set the event types for POST requests when multiple request types use the same route' do
56
+ post :my_path, attribution
57
+ expect(published_message[:path]).to eq('/my_path')
58
+ expect(published_message[:request_type]).to eq('POST')
59
+ expect(published_message[:event]).to eq('Customer Submitted')
60
+ end
61
+
62
+ it 'should properly set the event types for DELETE requests when multiple request types use the same route' do
63
+ delete :my_path, attribution
64
+ expect(published_message[:path]).to eq('/my_path')
65
+ expect(published_message[:request_type]).to eq('DELETE')
66
+ expect(published_message[:event]).to eq('Customer Left')
67
+ end
68
+
69
+ it 'should correctly set the user agent information in the message' do
70
+ get :my_path, attribution
71
+ user_agent = UserAgent[controller.request.user_agent]
72
+ expect(published_message[:user_agent_id]).to eq user_agent.id
73
+ expect(published_message[:user_agent]).to eq user_agent.user_agent
74
+ end
75
+
76
+ it 'should correctly set the page view information in the message' do
77
+ get :my_path, attribution
78
+ expect(published_message[:page_view_id]).to eq page_view.id
79
+ expect(published_message[:created_at]).to eq page_view.created_at
80
+ expect(published_message[:created_at]).to_not be_nil
81
+ end
82
+
83
+ it 'should correctly set the visit information in the message' do
84
+ get :my_path, attribution
85
+ visit = page_view.visit
86
+ visitor = page_view.visit.visitor
87
+ expect(published_message[:visit_id]).to eq visit.id
88
+ expect(published_message[:cookie_id]).to eq visit.cookie_id
89
+ expect(published_message[:ip_address_id]).to eq visitor.ip_address_id
90
+ expect(published_message[:ip_address]).to eq visitor.ip_address.to_s
91
+ end
92
+ end
93
+ end
94
+ end
@@ -114,8 +114,18 @@ module Landable
114
114
  end
115
115
 
116
116
  context 'no referer' do
117
+ let(:referer) { double('referer', { path: nil}) }
117
118
  let(:visit) { double('visit', { referer: nil }) }
118
119
 
120
+ describe '#referer_uri_path' do
121
+ it 'should return empty string' do
122
+ tracker = Landable::Traffic::UserTracker.new controller
123
+ tracker.stub(:referer_uri) { referer }
124
+
125
+ tracker.send(:referer_uri_path).should == ''
126
+ end
127
+ end
128
+
119
129
  describe '#visit_referer_domain' do
120
130
  it 'should return nil' do
121
131
  tracker = Landable::Traffic::UserTracker.new controller
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: landable
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.12.3
4
+ version: 1.13.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Team Trogdor
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-03-12 00:00:00.000000000 Z
11
+ date: 2015-04-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -539,6 +539,7 @@ files:
539
539
  - lib/landable/seeds.rb
540
540
  - lib/landable/traffic.rb
541
541
  - lib/landable/traffic/crawl_tracker.rb
542
+ - lib/landable/traffic/event_publisher.rb
542
543
  - lib/landable/traffic/noop_tracker.rb
543
544
  - lib/landable/traffic/ping_tracker.rb
544
545
  - lib/landable/traffic/scan_tracker.rb
@@ -620,6 +621,7 @@ files:
620
621
  - spec/dummy/db/.keep
621
622
  - spec/dummy/db/structure.sql
622
623
  - spec/dummy/lib/assets/.keep
624
+ - spec/dummy/lib/bunny_messaging_service.rb
623
625
  - spec/dummy/log/.keep
624
626
  - spec/dummy/public/404.html
625
627
  - spec/dummy/public/422.html
@@ -640,6 +642,7 @@ files:
640
642
  - spec/fixtures/assets/small.pdf
641
643
  - spec/helpers/pages_helper_spec.rb
642
644
  - spec/lib/landable/configuration_spec.rb
645
+ - spec/lib/landable/event_publisher_spec.rb
643
646
  - spec/lib/landable/layout_spec.rb
644
647
  - spec/lib/landable/liquid_spec.rb
645
648
  - spec/lib/landable/migration_spec.rb
@@ -779,6 +782,7 @@ test_files:
779
782
  - spec/dummy/db/.keep
780
783
  - spec/dummy/db/structure.sql
781
784
  - spec/dummy/lib/assets/.keep
785
+ - spec/dummy/lib/bunny_messaging_service.rb
782
786
  - spec/dummy/log/.keep
783
787
  - spec/dummy/public/404.html
784
788
  - spec/dummy/public/422.html
@@ -799,6 +803,7 @@ test_files:
799
803
  - spec/fixtures/assets/small.pdf
800
804
  - spec/helpers/pages_helper_spec.rb
801
805
  - spec/lib/landable/configuration_spec.rb
806
+ - spec/lib/landable/event_publisher_spec.rb
802
807
  - spec/lib/landable/layout_spec.rb
803
808
  - spec/lib/landable/liquid_spec.rb
804
809
  - spec/lib/landable/migration_spec.rb