forest_liana 2.9.2 → 2.10.0

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: aec5c04b564d1ea85d2af7b7e097a27148996842
4
- data.tar.gz: 012fff66c5d6457601ba654b6eff4e4d07652022
3
+ metadata.gz: b0a1f4647f0e31bc9e84c3ded9ba550c9b3d7fd7
4
+ data.tar.gz: 0aa2dadfd408b34049411e9945943e127a5b9b8e
5
5
  SHA512:
6
- metadata.gz: 19218a4d18faff38146343fae86be93737b8c6caf716f779b7772b1c6030a101aebe82b29b5029f65600854f75c356fbdc0d48e223bb5dfce790d2057e3f559a
7
- data.tar.gz: 5b296b887174352eeb49857b1e6c8f34789d4f951e12d9370616417e79d7df21ec2487fd7301c8d12d079f2fdc58a958eeaa5d35964d752361f5c58abb9f260a
6
+ metadata.gz: fb35fc0d3851ebedf7ebb37739847a09dc6352e3be6b62da28dcda3ca4f758db88795dc98d4a87c0c2b6b66ab0620f5fffcc3b0d3776e3db4d1944cb7b067b2d
7
+ data.tar.gz: e79eebb6fdecb583d82f4cdc7326845ca0a958ba6044b159fcf5336c5747c0f16ccea010bc700b4e295638fee4a6b458a76198af256ae9273aad4ba009ca7afe
@@ -0,0 +1,27 @@
1
+ module ForestLiana
2
+ class MixpanelController < ForestLiana::ApplicationController
3
+ def last_events
4
+ collection_name = params[:collection]
5
+ mapping = ForestLiana.integrations[:mixpanel][:mapping]
6
+ mapping_for_current_collection = mapping.find { |item| item.start_with?(collection_name) }
7
+ field_name = mapping_for_current_collection.split('.')[1]
8
+ id = params[:id]
9
+ field_value = collection_name.constantize.find_by('id': id)[field_name]
10
+
11
+ getter = ForestLiana::MixpanelLastEventsGetter.new(params)
12
+ getter.perform(field_name, field_value)
13
+
14
+ custom_properties = ForestLiana.integrations[:mixpanel][:custom_properties]
15
+ MixpanelEventSerializer.attributes(*custom_properties)
16
+
17
+ render serializer: nil, json: serialize_models(getter.records, {
18
+ context: { type: get_serializer_type('mixpanel_events') },
19
+ count: getter.count,
20
+ })
21
+ end
22
+
23
+ def get_serializer_type(suffix)
24
+ "#{params[:collection]}_#{suffix}"
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,24 @@
1
+ module ForestLiana
2
+ class MixpanelEventSerializer
3
+ include JSONAPI::Serializer
4
+
5
+ attribute :id
6
+ attribute :event
7
+ attribute :city
8
+ attribute :region
9
+ attribute :timezone
10
+ attribute :os
11
+ attribute :osVersion
12
+ attribute :country
13
+ attribute :date
14
+ attribute :browser
15
+
16
+ def self_link
17
+ nil
18
+ end
19
+
20
+ def type
21
+ @options[:context][:type] || 'mixpanel_events'
22
+ end
23
+ end
24
+ end
@@ -41,6 +41,8 @@ module ForestLiana
41
41
  "ForestLiana::ActionSerializer"
42
42
  elsif active_record_class == ForestLiana::Model::Segment
43
43
  "ForestLiana::SegmentSerializer"
44
+ elsif active_record_class == ForestLiana::MixpanelEvent
45
+ "ForestLiana::MixpanelEventSerializer"
44
46
  else
45
47
  serializer_name = self.build_serializer_name(active_record_class)
46
48
  "ForestLiana::UserSpace::#{serializer_name}"
@@ -109,6 +111,13 @@ module ForestLiana
109
111
  end
110
112
  end
111
113
 
114
+ if mixpanel_integration?
115
+ case attribute_name
116
+ when :mixpanel_last_events
117
+ ret[:href] = "/forest/#{ForestLiana.name_for(object.class)}/#{object.id}/mixpanel_last_events"
118
+ end
119
+ end
120
+
112
121
  if ret[:href].blank?
113
122
  begin
114
123
  if @options[:include].try(:include?, attribute_name.to_s)
@@ -151,6 +160,21 @@ module ForestLiana
151
160
  false
152
161
  end
153
162
  end
163
+
164
+ def mixpanel_integration?
165
+ mapping = ForestLiana.integrations
166
+ .try(:[], :mixpanel)
167
+ .try(:[], :mapping)
168
+
169
+ if mapping
170
+ collection_names = mapping.map do |collection_name_and_field|
171
+ collection_name_and_field.split('.')[0]
172
+ end
173
+ collection_names.include?(object.class.name)
174
+ else
175
+ false
176
+ end
177
+ end
154
178
  }
155
179
 
156
180
  unless @is_smart_collection
@@ -248,6 +272,11 @@ module ForestLiana
248
272
  serializer.send(:has_many, :stripe_bank_accounts) { }
249
273
  end
250
274
 
275
+ # Mixpanel
276
+ if has_mixpanel_integration?(active_record_class.name)
277
+ serializer.send(:has_many, :mixpanel_last_events) { }
278
+ end
279
+
251
280
  ForestLiana::SerializerFactory.define_serializer(active_record_class,
252
281
  serializer)
253
282
 
@@ -287,6 +316,21 @@ module ForestLiana
287
316
  end
288
317
  end
289
318
 
319
+ def has_mixpanel_integration?(collection_name)
320
+ mapping = ForestLiana.integrations
321
+ .try(:[], :mixpanel)
322
+ .try(:[], :mapping)
323
+
324
+ if mapping
325
+ collection_names = mapping.map do |collection_name_and_field|
326
+ collection_name_and_field.split('.')[0]
327
+ end
328
+ collection_names.include?(collection_name)
329
+ else
330
+ false
331
+ end
332
+ end
333
+
290
334
  def serializer_association(association)
291
335
  case association.macro
292
336
  when :has_one, :belongs_to
@@ -0,0 +1,87 @@
1
+ module ForestLiana
2
+ class MixpanelLastEventsGetter < IntegrationBaseGetter
3
+ attr_accessor :record
4
+
5
+ def initialize(params)
6
+ @params = params
7
+ api_secret = ForestLiana.integrations[:mixpanel][:api_secret]
8
+ @custom_properties = ForestLiana.integrations[:mixpanel][:custom_properties]
9
+ @mixpanel = Mixpanel::Client.new(api_secret: api_secret)
10
+ end
11
+
12
+ def perform(field_name, field_value)
13
+ result = @mixpanel.request(
14
+ 'jql',
15
+ script: "function main() {
16
+ return People().filter(function (user) {
17
+ return user.properties.$email == '#{field_value}';
18
+ });
19
+ }"
20
+ )
21
+
22
+ if result.length == 0
23
+ @records = []
24
+ return
25
+ end
26
+
27
+ from_date = (DateTime.now << 6).strftime("%Y-%m-%d")
28
+ to_date = DateTime.now.strftime("%Y-%m-%d")
29
+ distinct_id = result[0]['distinct_id']
30
+
31
+ result = @mixpanel.request(
32
+ 'stream/query',
33
+ from_date: from_date,
34
+ to_date: to_date,
35
+ distinct_ids: [distinct_id],
36
+ limit: 100
37
+ )
38
+
39
+ if result['status'] != 'ok'
40
+ FOREST_LOGGER.error "Cannot retrieve the Mixpanel last events"
41
+ @records = []
42
+ return
43
+ end
44
+
45
+ if result.length == 0
46
+ @records = []
47
+ return
48
+ end
49
+
50
+ @records = process_result(result['results']['events'])
51
+ end
52
+
53
+ def process_result(events)
54
+ events.reverse.map { |event|
55
+ properties = event['properties']
56
+
57
+ new_event = {
58
+ 'id' => SecureRandom.uuid,
59
+ 'event' => event['event'],
60
+ 'city' => properties['$city'],
61
+ 'region' => properties['$region'],
62
+ 'timezone' => properties['$timezone'],
63
+ 'os' => properties['$os'],
64
+ 'osVersion' => properties['$os_version'],
65
+ 'country' => properties['mp_country_code'],
66
+ 'browser' => properties['browser'],
67
+ }
68
+
69
+ time = properties['time'].to_s
70
+ new_event['date'] = DateTime.strptime(time,'%s').strftime("%Y-%m-%dT%H:%M:%S%z")
71
+
72
+ custom_attributes = event['properties'].select { |key, _| @custom_properties.include? key }
73
+ new_event = new_event.merge(custom_attributes)
74
+
75
+ ForestLiana::MixpanelEvent.new(new_event)
76
+ }
77
+ end
78
+
79
+ def records
80
+ @records
81
+ end
82
+
83
+ def count
84
+ @records.count
85
+ end
86
+ end
87
+ end
@@ -179,6 +179,28 @@ module ForestLiana
179
179
  end
180
180
  end
181
181
 
182
+ # NOTICE: Add Mixpanel field
183
+ mixpanel_mapping = ForestLiana.integrations
184
+ .try(:[], :mixpanel)
185
+ .try(:[], :mapping)
186
+
187
+ if mixpanel_mapping && mixpanel_mapping
188
+ .select { |mapping| mapping.split('.')[0] == @model.name }
189
+ .size > 0
190
+
191
+ model_name = ForestLiana.name_for(@model)
192
+
193
+ collection.fields << {
194
+ field: :mixpanel_last_events,
195
+ type: ['String'],
196
+ reference: "#{model_name}_mixpanel_events.id",
197
+ column: nil,
198
+ 'is-filterable': false,
199
+ 'display-name': 'Last events',
200
+ integration: 'mixpanel',
201
+ }
202
+ end
203
+
182
204
  # NOTICE: Add Paperclip url attributes
183
205
  if @model.respond_to?(:attachment_definitions)
184
206
  @model.attachment_definitions.each do |key, value|
data/config/routes.rb CHANGED
@@ -39,6 +39,9 @@ ForestLiana::Engine.routes.draw do
39
39
  get ':collection/:id/intercom_attributes' => 'intercom#attributes'
40
40
  get '(*collection)_intercom_conversations/:conversation_id' => 'intercom#conversation'
41
41
 
42
+ # Mixpanel Integration
43
+ get ':collection/:id/mixpanel_last_events' => 'mixpanel#last_events'
44
+
42
45
  # Devise support
43
46
  post '/actions/change-password' => 'devise#change_password'
44
47
 
data/lib/forest_liana.rb CHANGED
@@ -4,6 +4,9 @@ module Forest
4
4
  end
5
5
 
6
6
  module ForestLiana
7
+
8
+ autoload :MixpanelEvent, 'forest_liana/mixpanel_event'
9
+
7
10
  module UserSpace
8
11
  end
9
12
 
@@ -113,6 +113,17 @@ module ForestLiana
113
113
  'Please go to https://doc.forestadmin.com for more information.'
114
114
  end
115
115
  end
116
+
117
+ if mixpanel_integration?
118
+ if mixpanel_integration_valid?
119
+ ForestLiana.integrations[:mixpanel][:mapping] =
120
+ cast_to_array(ForestLiana.integrations[:mixpanel][:mapping])
121
+ @integration_mixpanel_valid = true
122
+ else
123
+ FOREST_LOGGER.error 'Cannot setup properly your Mixpanel integration. ' \
124
+ 'Please go to https://doc.forestadmin.com for more information.'
125
+ end
126
+ end
116
127
  end
117
128
 
118
129
  def namespace_duplicated_models
@@ -149,6 +160,12 @@ module ForestLiana
149
160
  setup_intercom_integration collection_name
150
161
  end
151
162
  end
163
+
164
+ if @integration_mixpanel_valid
165
+ ForestLiana.integrations[:mixpanel][:mapping].each do |collection_name|
166
+ setup_mixpanel_integration collection_name
167
+ end
168
+ end
152
169
  end
153
170
 
154
171
  def require_lib_forest_liana
@@ -522,6 +539,61 @@ module ForestLiana
522
539
  is_deprecated
523
540
  end
524
541
 
542
+ def setup_mixpanel_integration(collection_name_and_field)
543
+ collection_name = collection_name_and_field.split('.')[0]
544
+ model_name = ForestLiana.name_for(collection_name.constantize)
545
+ collection_display_name = model_name.capitalize
546
+
547
+ field_attributes = { 'is-filterable': false , 'is-virtual': true, 'is-sortable': false }
548
+
549
+ fields = [
550
+ { field: :id, type: 'String' },
551
+ { field: :event, type: 'String' },
552
+ { field: :date, type: 'Date' },
553
+ { field: :city, type: 'String' },
554
+ { field: :region, type: 'String' },
555
+ { field: :country, type: 'String' },
556
+ { field: :timezone, type: 'String' },
557
+ { field: :os, type: 'String' },
558
+ { field: :osVersion, type: 'String' },
559
+ { field: :browser, type: 'String' },
560
+ { field: :browserVersion, type: 'String' },
561
+ ]
562
+
563
+ fields = fields.map { |field| field.merge(field_attributes) }
564
+
565
+ custom_properties = ForestLiana.integrations[:mixpanel][:custom_properties]
566
+ custom_properties = custom_properties.map { |property|
567
+ { field: property.to_sym, type: 'String' }.merge(field_attributes)
568
+ }
569
+
570
+ fields = fields.concat(custom_properties)
571
+
572
+ ForestLiana.apimap << ForestLiana::Model::Collection.new({
573
+ name: "#{model_name}_mixpanel_events",
574
+ display_name: "#{collection_display_name} Events",
575
+ icon: 'mixpanel',
576
+ integration: 'mixpanel',
577
+ is_virtual: true,
578
+ is_read_only: true,
579
+ only_for_relationships: true,
580
+ pagination_type: 'cursor',
581
+ fields: fields
582
+ })
583
+ end
584
+
585
+ def mixpanel_integration?
586
+ ForestLiana.integrations
587
+ .try(:[], :mixpanel)
588
+ .present?
589
+ end
590
+
591
+ def mixpanel_integration_valid?
592
+ integration = ForestLiana.integrations.try(:[], :mixpanel)
593
+ integration.present? && integration.has_key?(:api_secret) &&
594
+ integration.has_key?(:mapping)
595
+ end
596
+
525
597
  def forest_url
526
598
  ENV['FOREST_URL'] || 'https://api.forestadmin.com';
527
599
  end
@@ -0,0 +1,4 @@
1
+ module ForestLiana
2
+ class MixpanelEvent < OpenStruct
3
+ end
4
+ end
@@ -1,3 +1,3 @@
1
1
  module ForestLiana
2
- VERSION = "2.9.2"
2
+ VERSION = "2.10.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: forest_liana
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.9.2
4
+ version: 2.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sandro Munda
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-07-04 00:00:00.000000000 Z
11
+ date: 2018-07-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -144,6 +144,7 @@ files:
144
144
  - app/controllers/forest_liana/base_controller.rb
145
145
  - app/controllers/forest_liana/devise_controller.rb
146
146
  - app/controllers/forest_liana/intercom_controller.rb
147
+ - app/controllers/forest_liana/mixpanel_controller.rb
147
148
  - app/controllers/forest_liana/resources_controller.rb
148
149
  - app/controllers/forest_liana/router.rb
149
150
  - app/controllers/forest_liana/sessions_controller.rb
@@ -161,6 +162,7 @@ files:
161
162
  - app/serializers/forest_liana/collection_serializer.rb
162
163
  - app/serializers/forest_liana/intercom_attribute_serializer.rb
163
164
  - app/serializers/forest_liana/intercom_conversation_serializer.rb
165
+ - app/serializers/forest_liana/mixpanel_event_serializer.rb
164
166
  - app/serializers/forest_liana/segment_serializer.rb
165
167
  - app/serializers/forest_liana/serializer_factory.rb
166
168
  - app/serializers/forest_liana/session_serializer.rb
@@ -186,6 +188,7 @@ files:
186
188
  - app/services/forest_liana/intercom_conversations_getter.rb
187
189
  - app/services/forest_liana/line_stat_getter.rb
188
190
  - app/services/forest_liana/live_query_checker.rb
191
+ - app/services/forest_liana/mixpanel_last_events_getter.rb
189
192
  - app/services/forest_liana/operator_date_interval_parser.rb
190
193
  - app/services/forest_liana/operator_value_parser.rb
191
194
  - app/services/forest_liana/permissions_checker.rb
@@ -223,6 +226,7 @@ files:
223
226
  - lib/forest_liana/bootstraper.rb
224
227
  - lib/forest_liana/collection.rb
225
228
  - lib/forest_liana/engine.rb
229
+ - lib/forest_liana/mixpanel_event.rb
226
230
  - lib/forest_liana/version.rb
227
231
  - lib/generators/forest_liana/install_generator.rb
228
232
  - test/dummy/README.rdoc
@@ -291,8 +295,6 @@ files:
291
295
  - test/dummy/db/migrate/20160628173505_add_timestamps.rb
292
296
  - test/dummy/db/migrate/20170614141921_create_serialize_field.rb
293
297
  - test/dummy/db/schema.rb
294
- - test/dummy/db/test.sqlite3
295
- - test/dummy/log/test.log
296
298
  - test/dummy/public/404.html
297
299
  - test/dummy/public/422.html
298
300
  - test/dummy/public/500.html
@@ -391,7 +393,6 @@ test_files:
391
393
  - test/dummy/public/500.html
392
394
  - test/dummy/public/404.html
393
395
  - test/dummy/db/schema.rb
394
- - test/dummy/db/test.sqlite3
395
396
  - test/dummy/db/migrate/20150608133038_create_belongs_to_field.rb
396
397
  - test/dummy/db/migrate/20150608130516_create_date_field.rb
397
398
  - test/dummy/db/migrate/20160627172951_create_tree.rb
@@ -410,7 +411,6 @@ test_files:
410
411
  - test/dummy/db/migrate/20150608131603_create_decimal_field.rb
411
412
  - test/dummy/db/migrate/20150623115554_create_has_many_class_name_field.rb
412
413
  - test/dummy/db/migrate/20150616150629_create_polymorphic_field.rb
413
- - test/dummy/log/test.log
414
414
  - test/dummy/README.rdoc
415
415
  - test/integration/navigation_test.rb
416
416
  - test/forest_liana_test.rb