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 +4 -4
- data/app/controllers/forest_liana/mixpanel_controller.rb +27 -0
- data/app/serializers/forest_liana/mixpanel_event_serializer.rb +24 -0
- data/app/serializers/forest_liana/serializer_factory.rb +44 -0
- data/app/services/forest_liana/mixpanel_last_events_getter.rb +87 -0
- data/app/services/forest_liana/schema_adapter.rb +22 -0
- data/config/routes.rb +3 -0
- data/lib/forest_liana.rb +3 -0
- data/lib/forest_liana/bootstraper.rb +72 -0
- data/lib/forest_liana/mixpanel_event.rb +4 -0
- data/lib/forest_liana/version.rb +1 -1
- metadata +6 -6
- data/test/dummy/db/test.sqlite3 +0 -0
- data/test/dummy/log/test.log +0 -56094
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b0a1f4647f0e31bc9e84c3ded9ba550c9b3d7fd7
|
4
|
+
data.tar.gz: 0aa2dadfd408b34049411e9945943e127a5b9b8e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
@@ -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
|
data/lib/forest_liana/version.rb
CHANGED
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.
|
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-
|
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
|