forest_liana 2.9.2 → 2.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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