forest_liana 2.15.8 → 3.0.0.pre.beta.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Rakefile +1 -1
- data/app/controllers/forest_liana/resources_controller.rb +9 -17
- data/app/controllers/forest_liana/stats_controller.rb +4 -14
- data/app/models/forest_liana/model/action.rb +57 -3
- data/app/models/forest_liana/model/collection.rb +37 -9
- data/app/models/forest_liana/model/segment.rb +1 -1
- data/app/serializers/forest_liana/schema_serializer.rb +83 -0
- data/app/serializers/forest_liana/serializer_factory.rb +13 -48
- data/app/services/forest_liana/apimap_sorter.rb +88 -48
- data/app/services/forest_liana/base_getter.rb +1 -7
- data/app/services/forest_liana/has_many_getter.rb +14 -15
- data/app/services/forest_liana/line_stat_getter.rb +5 -1
- data/app/services/forest_liana/operator_value_parser.rb +1 -46
- data/app/services/forest_liana/pie_stat_getter.rb +2 -2
- data/app/services/forest_liana/query_stat_getter.rb +3 -25
- data/app/services/forest_liana/resource_getter.rb +8 -2
- data/app/services/forest_liana/resources_getter.rb +13 -27
- data/app/services/forest_liana/schema_adapter.rb +65 -64
- data/app/services/forest_liana/search_query_builder.rb +24 -5
- data/app/services/forest_liana/stat_getter.rb +8 -1
- data/app/services/forest_liana/value_stat_getter.rb +2 -2
- data/lib/forest_liana.rb +2 -0
- data/lib/forest_liana/bootstraper.rb +119 -122
- data/lib/forest_liana/collection.rb +41 -12
- data/lib/forest_liana/engine.rb +16 -11
- data/lib/forest_liana/json_printer.rb +54 -0
- data/lib/forest_liana/schema_file_updater.rb +141 -0
- data/lib/forest_liana/version.rb +1 -1
- data/test/dummy/config/routes.rb +1 -0
- data/test/dummy/db/test.sqlite3 +0 -0
- data/test/dummy/log/test.log +116056 -3826
- data/test/integration/navigation_test.rb +10 -0
- data/test/routing/route_test.rb +2 -0
- data/test/services/forest_liana/resources_getter_test.rb +3 -3
- data/test/services/forest_liana/schema_adapter_test.rb +50 -49
- metadata +11 -111
- data/app/helpers/forest_liana/query_helper.rb +0 -30
- data/app/helpers/forest_liana/schema_helper.rb +0 -8
- data/app/serializers/forest_liana/action_serializer.rb +0 -21
- data/app/serializers/forest_liana/collection_serializer.rb +0 -33
- data/app/serializers/forest_liana/segment_serializer.rb +0 -15
- data/app/services/forest_liana/leaderboard_stat_getter.rb +0 -44
- data/app/services/forest_liana/objective_stat_getter.rb +0 -10
- data/spec/dummy/README.rdoc +0 -28
- data/spec/dummy/Rakefile +0 -6
- data/spec/dummy/app/assets/javascripts/application.js +0 -13
- data/spec/dummy/app/assets/stylesheets/application.css +0 -15
- data/spec/dummy/app/config/routes.rb +0 -3
- data/spec/dummy/app/controllers/application_controller.rb +0 -5
- data/spec/dummy/app/helpers/application_helper.rb +0 -2
- data/spec/dummy/app/models/island.rb +0 -5
- data/spec/dummy/app/models/tree.rb +0 -5
- data/spec/dummy/app/models/user.rb +0 -4
- data/spec/dummy/app/views/layouts/application.html.erb +0 -14
- data/spec/dummy/bin/bundle +0 -3
- data/spec/dummy/bin/rails +0 -4
- data/spec/dummy/bin/rake +0 -4
- data/spec/dummy/bin/setup +0 -29
- data/spec/dummy/config.ru +0 -4
- data/spec/dummy/config/application.rb +0 -26
- data/spec/dummy/config/boot.rb +0 -5
- data/spec/dummy/config/database.yml +0 -25
- data/spec/dummy/config/environment.rb +0 -5
- data/spec/dummy/config/environments/development.rb +0 -41
- data/spec/dummy/config/environments/production.rb +0 -79
- data/spec/dummy/config/environments/test.rb +0 -42
- data/spec/dummy/config/initializers/assets.rb +0 -11
- data/spec/dummy/config/initializers/backtrace_silencers.rb +0 -7
- data/spec/dummy/config/initializers/cookies_serializer.rb +0 -3
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +0 -4
- data/spec/dummy/config/initializers/forest_liana.rb +0 -2
- data/spec/dummy/config/initializers/inflections.rb +0 -16
- data/spec/dummy/config/initializers/mime_types.rb +0 -4
- data/spec/dummy/config/initializers/session_store.rb +0 -3
- data/spec/dummy/config/initializers/wrap_parameters.rb +0 -14
- data/spec/dummy/config/routes.rb +0 -3
- data/spec/dummy/config/secrets.yml +0 -22
- data/spec/dummy/db/migrate/20190226172951_create_user.rb +0 -9
- data/spec/dummy/db/migrate/20190226173051_create_isle.rb +0 -10
- data/spec/dummy/db/migrate/20190226174951_create_tree.rb +0 -12
- data/spec/dummy/db/schema.rb +0 -42
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/log/development.log +0 -80
- data/spec/dummy/log/test.log +0 -26139
- data/spec/helpers/forest_liana/query_helper_spec.rb +0 -74
- data/spec/helpers/forest_liana/schema_helper_spec.rb +0 -13
- data/spec/rails_helper.rb +0 -61
- data/spec/requests/resources_spec.rb +0 -97
- data/spec/services/forest_liana/apimap_sorter_spec.rb +0 -172
- data/spec/services/forest_liana/ip_whitelist_checker_spec.rb +0 -203
- data/spec/services/forest_liana/schema_adapter_spec.rb +0 -17
- data/spec/spec_helper.rb +0 -99
@@ -1,7 +1,6 @@
|
|
1
1
|
module ForestLiana
|
2
2
|
class ResourcesGetter < BaseGetter
|
3
3
|
attr_reader :search_query_builder
|
4
|
-
attr_reader :includes
|
5
4
|
attr_reader :records_count
|
6
5
|
|
7
6
|
def initialize(resource, params)
|
@@ -10,24 +9,22 @@ module ForestLiana
|
|
10
9
|
@count_needs_includes = false
|
11
10
|
@collection_name = ForestLiana.name_for(@resource)
|
12
11
|
@collection = get_collection(@collection_name)
|
13
|
-
@fields_to_serialize = get_fields_to_serialize
|
14
12
|
@field_names_requested = field_names_requested
|
15
13
|
get_segment()
|
16
|
-
|
17
|
-
@search_query_builder = SearchQueryBuilder.new(@params, @includes, @collection)
|
14
|
+
@search_query_builder = SearchQueryBuilder.new(@params, includes, @collection)
|
18
15
|
|
19
16
|
prepare_query()
|
20
17
|
end
|
21
18
|
|
22
19
|
def perform
|
23
|
-
@records = @records.eager_load(
|
20
|
+
@records = @records.eager_load(includes)
|
24
21
|
@records_sorted = sort_query
|
25
22
|
end
|
26
23
|
|
27
24
|
def count
|
28
|
-
# NOTICE: For performance reasons, do not eager load the data if there is
|
29
|
-
# filters on associations.
|
30
|
-
@records_count = @count_needs_includes ? @records.eager_load(
|
25
|
+
# NOTICE: For performance reasons, do not eager load the data if there is
|
26
|
+
# no search or filters on associations.
|
27
|
+
@records_count = @count_needs_includes ? @records.eager_load(includes).count : @records.count
|
31
28
|
end
|
32
29
|
|
33
30
|
def query_for_batch
|
@@ -38,10 +35,10 @@ module ForestLiana
|
|
38
35
|
@records_sorted.offset(offset).limit(limit).to_a
|
39
36
|
end
|
40
37
|
|
41
|
-
def
|
42
|
-
|
43
|
-
|
44
|
-
|
38
|
+
def includes
|
39
|
+
includes = SchemaUtils.one_associations(@resource)
|
40
|
+
.select { |association| SchemaUtils.model_included?(association.klass) }
|
41
|
+
.map(&:name)
|
45
42
|
includes_for_smart_search = []
|
46
43
|
|
47
44
|
if @collection && @collection.search_fields
|
@@ -57,26 +54,14 @@ module ForestLiana
|
|
57
54
|
end
|
58
55
|
|
59
56
|
if @field_names_requested
|
60
|
-
|
57
|
+
(includes & @field_names_requested).concat(includes_for_smart_search)
|
61
58
|
else
|
62
|
-
|
59
|
+
includes
|
63
60
|
end
|
64
61
|
end
|
65
62
|
|
66
|
-
def includes_for_serialization
|
67
|
-
super & @fields_to_serialize.map(&:to_s)
|
68
|
-
end
|
69
|
-
|
70
63
|
private
|
71
64
|
|
72
|
-
def get_fields_to_serialize
|
73
|
-
if @params[:fields] && @params[:fields][@collection_name]
|
74
|
-
@params[:fields][@collection_name].split(',').map { |name| name.to_sym }
|
75
|
-
else
|
76
|
-
[]
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
65
|
def get_segment
|
81
66
|
if @params[:segment]
|
82
67
|
@segment = @collection.segments.find do |segment|
|
@@ -107,7 +92,8 @@ module ForestLiana
|
|
107
92
|
associations_for_query << @params[:sort].split('.').first.to_sym
|
108
93
|
end
|
109
94
|
|
110
|
-
@
|
95
|
+
field_names = @params[:fields][@collection_name].split(',').map { |name| name.to_sym }
|
96
|
+
field_names | associations_for_query
|
111
97
|
end
|
112
98
|
|
113
99
|
def search_query
|
@@ -98,8 +98,7 @@ module ForestLiana
|
|
98
98
|
def add_columns
|
99
99
|
@model.columns.each do |column|
|
100
100
|
unless is_sti_column_of_child_model?(column)
|
101
|
-
|
102
|
-
collection.fields << field_schema unless field_schema.nil?
|
101
|
+
collection.fields << get_schema_for_column(column)
|
103
102
|
end
|
104
103
|
end
|
105
104
|
|
@@ -112,20 +111,18 @@ module ForestLiana
|
|
112
111
|
collection.fields << {
|
113
112
|
field: :intercom_conversations,
|
114
113
|
type: ['String'],
|
115
|
-
relationship: 'HasMany',
|
116
114
|
reference: "#{model_name}_intercom_conversations.id",
|
117
115
|
column: nil,
|
118
|
-
|
116
|
+
is_filterable: false,
|
119
117
|
integration: 'intercom'
|
120
118
|
}
|
121
119
|
|
122
|
-
|
120
|
+
collection.fields << {
|
123
121
|
field: :intercom_attributes,
|
124
122
|
type: 'String',
|
125
|
-
relationship: 'HasOne',
|
126
123
|
reference: "#{model_name}_intercom_attributes.id",
|
127
124
|
column: nil,
|
128
|
-
|
125
|
+
is_filterable: false,
|
129
126
|
integration: 'intercom'
|
130
127
|
}
|
131
128
|
end
|
@@ -144,50 +141,45 @@ module ForestLiana
|
|
144
141
|
collection.fields << {
|
145
142
|
field: :stripe_payments,
|
146
143
|
type: ['String'],
|
147
|
-
relationship: 'HasMany',
|
148
144
|
reference: "#{model_name}_stripe_payments.id",
|
149
145
|
column: nil,
|
150
|
-
|
146
|
+
is_filterable: false,
|
151
147
|
integration: 'stripe'
|
152
148
|
}
|
153
149
|
|
154
150
|
collection.fields << {
|
155
151
|
field: :stripe_invoices,
|
156
152
|
type: ['String'],
|
157
|
-
relationship: 'HasMany',
|
158
153
|
reference: "#{model_name}_stripe_invoices.id",
|
159
154
|
column: nil,
|
160
|
-
|
155
|
+
is_filterable: false,
|
161
156
|
integration: 'stripe'
|
162
157
|
}
|
163
158
|
|
164
159
|
collection.fields << {
|
165
160
|
field: :stripe_cards,
|
166
161
|
type: ['String'],
|
167
|
-
relationship: 'HasMany',
|
168
162
|
reference: "#{model_name}_stripe_cards.id",
|
169
163
|
column: nil,
|
170
|
-
|
164
|
+
is_filterable: false,
|
171
165
|
integration: 'stripe'
|
172
166
|
}
|
173
167
|
|
174
168
|
collection.fields << {
|
175
169
|
field: :stripe_subscriptions,
|
176
170
|
type: ['String'],
|
177
|
-
relationship: 'HasMany',
|
178
171
|
reference: "#{model_name}_stripe_subscriptions.id",
|
179
172
|
column: nil,
|
180
|
-
|
173
|
+
is_filterable: false,
|
181
174
|
integration: 'stripe'
|
182
175
|
}
|
183
176
|
|
184
177
|
collection.fields << {
|
185
178
|
field: :stripe_bank_accounts,
|
186
179
|
type: ['String'],
|
187
|
-
relationship: 'HasMany',
|
188
180
|
reference: "#{model_name}_stripe_bank_accounts.id",
|
189
181
|
column: nil,
|
190
|
-
|
182
|
+
is_filterable: false,
|
191
183
|
integration: 'stripe'
|
192
184
|
}
|
193
185
|
end
|
@@ -207,11 +199,9 @@ module ForestLiana
|
|
207
199
|
collection.fields << {
|
208
200
|
field: :mixpanel_last_events,
|
209
201
|
type: ['String'],
|
210
|
-
relationship: 'HasMany',
|
211
202
|
reference: "#{model_name}_mixpanel_events.id",
|
212
203
|
column: nil,
|
213
|
-
|
214
|
-
'display-name': 'Last events',
|
204
|
+
is_filterable: false,
|
215
205
|
integration: 'mixpanel',
|
216
206
|
}
|
217
207
|
end
|
@@ -252,8 +242,7 @@ module ForestLiana
|
|
252
242
|
[:has_one, :belongs_to].include?(association.macro)
|
253
243
|
field[:reference] = get_reference_for(association)
|
254
244
|
field[:field] = association.name
|
255
|
-
field[:
|
256
|
-
field[:relationship] = get_relationship_type(association)
|
245
|
+
field[:inverse_of] = inverse_of(association)
|
257
246
|
# NOTICE: Create the fields of hasOne, HasMany, … relationships.
|
258
247
|
else
|
259
248
|
collection.fields << get_schema_for_association(association)
|
@@ -284,10 +273,22 @@ module ForestLiana
|
|
284
273
|
end
|
285
274
|
|
286
275
|
def get_schema_for_column(column)
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
276
|
+
schema = {
|
277
|
+
field: column.name,
|
278
|
+
type: get_type_for(column),
|
279
|
+
is_filterable: true,
|
280
|
+
is_sortable: true,
|
281
|
+
is_read_only: false,
|
282
|
+
is_required: false,
|
283
|
+
is_virtual: false,
|
284
|
+
default_value: nil,
|
285
|
+
integration: nil,
|
286
|
+
reference: nil,
|
287
|
+
inverse_of: nil,
|
288
|
+
relationships: nil,
|
289
|
+
widget: nil,
|
290
|
+
validations: []
|
291
|
+
}
|
291
292
|
add_enum_values_if_is_enum(schema, column)
|
292
293
|
add_enum_values_if_is_sti_model(schema, column)
|
293
294
|
add_default_value(schema, column)
|
@@ -298,17 +299,21 @@ module ForestLiana
|
|
298
299
|
{
|
299
300
|
field: association.name.to_s,
|
300
301
|
type: get_type_for_association(association),
|
301
|
-
relationship: get_relationship_type(association),
|
302
302
|
reference: "#{ForestLiana.name_for(association.klass)}.id",
|
303
|
-
|
304
|
-
|
303
|
+
inverse_of: inverse_of(association),
|
304
|
+
is_filterable: !is_many_association(association),
|
305
|
+
is_sortable: true,
|
306
|
+
is_read_only: false,
|
307
|
+
is_required: false,
|
308
|
+
is_virtual: false,
|
309
|
+
default_value: nil,
|
310
|
+
integration: nil,
|
311
|
+
relationships: nil,
|
312
|
+
widget: nil,
|
313
|
+
validations: []
|
305
314
|
}
|
306
315
|
end
|
307
316
|
|
308
|
-
def get_relationship_type(association)
|
309
|
-
association.macro.to_s.camelize
|
310
|
-
end
|
311
|
-
|
312
317
|
def get_type_for(column)
|
313
318
|
# NOTICE: Rails 3 do not have a defined_enums method
|
314
319
|
if @model.respond_to?(:defined_enums) &&
|
@@ -376,7 +381,7 @@ module ForestLiana
|
|
376
381
|
def add_default_value(column_schema, column)
|
377
382
|
# TODO: detect/introspect the attribute default value with Rails 5
|
378
383
|
# ex: attribute :email, :string, default: 'arnaud@forestadmin.com'
|
379
|
-
column_schema[
|
384
|
+
column_schema[:default_value] = column.default if column.default
|
380
385
|
end
|
381
386
|
|
382
387
|
def add_validations(column_schema, column)
|
@@ -387,8 +392,6 @@ module ForestLiana
|
|
387
392
|
end
|
388
393
|
|
389
394
|
if @model._validators? && @model._validators[column.name.to_sym].size > 0
|
390
|
-
column_schema[:validations] = []
|
391
|
-
|
392
395
|
@model._validators[column.name.to_sym].each do |validator|
|
393
396
|
# NOTICE: Do not consider conditional validations
|
394
397
|
next if validator.options[:if] || validator.options[:unless]
|
@@ -399,7 +402,7 @@ module ForestLiana
|
|
399
402
|
type: 'is present',
|
400
403
|
message: validator.options[:message]
|
401
404
|
}
|
402
|
-
column_schema[
|
405
|
+
column_schema[:is_required] = true
|
403
406
|
when ActiveModel::Validations::NumericalityValidator
|
404
407
|
validator.options.each do |option, value|
|
405
408
|
case option
|
@@ -418,33 +421,31 @@ module ForestLiana
|
|
418
421
|
end
|
419
422
|
end
|
420
423
|
when ActiveModel::Validations::LengthValidator
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
}
|
447
|
-
end
|
424
|
+
validator.options.each do |option, value|
|
425
|
+
case option
|
426
|
+
when :minimum
|
427
|
+
column_schema[:validations] << {
|
428
|
+
type: 'is longer than',
|
429
|
+
value: value,
|
430
|
+
message: validator.options[:message]
|
431
|
+
}
|
432
|
+
when :maximum
|
433
|
+
column_schema[:validations] << {
|
434
|
+
type: 'is shorter than',
|
435
|
+
value: value,
|
436
|
+
message: validator.options[:message]
|
437
|
+
}
|
438
|
+
when :is
|
439
|
+
column_schema[:validations] << {
|
440
|
+
type: 'is longer than',
|
441
|
+
value: value,
|
442
|
+
message: validator.options[:message]
|
443
|
+
}
|
444
|
+
column_schema[:validations] << {
|
445
|
+
type: 'is shorter than',
|
446
|
+
value: value,
|
447
|
+
message: validator.options[:message]
|
448
|
+
}
|
448
449
|
end
|
449
450
|
end
|
450
451
|
when ActiveModel::Validations::FormatValidator
|
@@ -14,8 +14,6 @@ module ForestLiana
|
|
14
14
|
|
15
15
|
def perform(resource)
|
16
16
|
@resource = @records = resource
|
17
|
-
@tables_associated_to_relations_name =
|
18
|
-
ForestLiana::QueryHelper.get_tables_associated_to_relations_name(@resource)
|
19
17
|
@records = search_param
|
20
18
|
@records = filter_param
|
21
19
|
@records = has_many_filter
|
@@ -89,7 +87,8 @@ module ForestLiana
|
|
89
87
|
end
|
90
88
|
|
91
89
|
if (@params['searchExtended'].to_i == 1)
|
92
|
-
|
90
|
+
SchemaUtils.one_associations(@resource).map(&:name).each do
|
91
|
+
|association|
|
93
92
|
if @collection.search_fields
|
94
93
|
association_search = @collection.search_fields.map do |field|
|
95
94
|
if field.include?('.') && field.split('.')[0] == association.to_s
|
@@ -261,8 +260,28 @@ module ForestLiana
|
|
261
260
|
end
|
262
261
|
|
263
262
|
def belongs_to_subfield_filter(field, value)
|
264
|
-
|
265
|
-
|
263
|
+
field, subfield = field.split(':')
|
264
|
+
|
265
|
+
association = @resource.reflect_on_association(field.to_sym)
|
266
|
+
return if association.blank?
|
267
|
+
|
268
|
+
operator, value = OperatorValueParser.parse(value)
|
269
|
+
filter = OperatorValueParser
|
270
|
+
.get_condition_end(subfield, operator, value, association.klass, @params[:timezone])
|
271
|
+
|
272
|
+
association_name = association.name.to_s
|
273
|
+
association_name_pluralized = association_name.pluralize
|
274
|
+
|
275
|
+
if [association_name, association_name_pluralized].include? association.table_name
|
276
|
+
# NOTICE: Default case. When the belongsTo association name and the referenced table name are identical.
|
277
|
+
association_name_for_condition = association.table_name
|
278
|
+
else
|
279
|
+
# NOTICE: When the the belongsTo association name and the referenced table name are identical.
|
280
|
+
# Format with the ActiveRecord query generator style.
|
281
|
+
association_name_for_condition = "#{association_name_pluralized}_#{@resource.table_name}"
|
282
|
+
end
|
283
|
+
|
284
|
+
@records.where("#{association_name_for_condition}.#{subfield} #{filter}")
|
266
285
|
end
|
267
286
|
|
268
287
|
def belongs_to_filter
|
@@ -5,7 +5,14 @@ module ForestLiana
|
|
5
5
|
def initialize(resource, params)
|
6
6
|
@resource = resource
|
7
7
|
@params = params
|
8
|
-
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def includes
|
13
|
+
SchemaUtils.one_associations(@resource)
|
14
|
+
.select { |association| SchemaUtils.model_included?(association.klass) }
|
15
|
+
.map(&:name)
|
9
16
|
end
|
10
17
|
end
|
11
18
|
end
|
@@ -4,8 +4,8 @@ module ForestLiana
|
|
4
4
|
|
5
5
|
def perform
|
6
6
|
return if @params[:aggregate].blank?
|
7
|
-
valueCurrent = get_resource().eager_load(
|
8
|
-
valuePrevious = get_resource().eager_load(
|
7
|
+
valueCurrent = get_resource().eager_load(includes)
|
8
|
+
valuePrevious = get_resource().eager_load(includes)
|
9
9
|
filter_date_interval = false
|
10
10
|
|
11
11
|
if @params[:filterType] && @params[:filters]
|
data/lib/forest_liana.rb
CHANGED
@@ -24,6 +24,7 @@ module ForestLiana
|
|
24
24
|
mattr_accessor :included_models
|
25
25
|
mattr_accessor :user_class_name
|
26
26
|
mattr_accessor :names_overriden
|
27
|
+
mattr_accessor :meta
|
27
28
|
# TODO: Remove once lianas prior to 2.0.0 are not supported anymore.
|
28
29
|
mattr_accessor :names_old_overriden
|
29
30
|
|
@@ -34,6 +35,7 @@ module ForestLiana
|
|
34
35
|
self.included_models = []
|
35
36
|
self.user_class_name = nil
|
36
37
|
self.names_overriden = {}
|
38
|
+
self.meta = {}
|
37
39
|
|
38
40
|
# TODO: Remove once lianas prior to 2.0.0 are not supported anymore.
|
39
41
|
self.names_old_overriden = {}
|
@@ -1,5 +1,8 @@
|
|
1
|
+
require_relative 'schema_file_updater'
|
2
|
+
|
1
3
|
module ForestLiana
|
2
4
|
class Bootstraper
|
5
|
+
SCHEMA_FILENAME = File.join(Dir.pwd, '.forestadmin-schema.json')
|
3
6
|
|
4
7
|
def initialize
|
5
8
|
@integration_stripe_valid = false
|
@@ -38,6 +41,26 @@ module ForestLiana
|
|
38
41
|
create_apimap
|
39
42
|
require_lib_forest_liana
|
40
43
|
format_and_validate_smart_actions
|
44
|
+
|
45
|
+
if Rails.env.development?
|
46
|
+
@collections_sent = ForestLiana.apimap.as_json
|
47
|
+
@meta_sent = ForestLiana.meta
|
48
|
+
SchemaFileUpdater.new(SCHEMA_FILENAME, @collections_sent, @meta_sent).perform()
|
49
|
+
else
|
50
|
+
if File.exists?(SCHEMA_FILENAME)
|
51
|
+
begin
|
52
|
+
content = JSON.parse(File.read(SCHEMA_FILENAME))
|
53
|
+
@collections_sent = content['collections']
|
54
|
+
@meta_sent = content['meta']
|
55
|
+
rescue JSON::JSONError
|
56
|
+
FOREST_LOGGER.error "The content of .forestadmin-schema.json file is not a correct JSON."
|
57
|
+
FOREST_LOGGER.error "The schema cannot be synchronized with Forest Admin servers."
|
58
|
+
end
|
59
|
+
else
|
60
|
+
FOREST_LOGGER.error "The .forestadmin-schema.json file does not exists."
|
61
|
+
FOREST_LOGGER.error "The schema cannot be synchronized with Forest Admin servers."
|
62
|
+
end
|
63
|
+
end
|
41
64
|
end
|
42
65
|
|
43
66
|
def is_sti_parent_model?(model)
|
@@ -148,7 +171,19 @@ module ForestLiana
|
|
148
171
|
end
|
149
172
|
end
|
150
173
|
|
174
|
+
def setup_forest_liana_meta
|
175
|
+
ForestLiana.meta = {
|
176
|
+
database_type: database_type,
|
177
|
+
framework_version: Gem.loaded_specs["rails"].version.version,
|
178
|
+
liana: 'forest-rails',
|
179
|
+
liana_version: liana_version,
|
180
|
+
orm_version: Gem.loaded_specs["activerecord"].version.version
|
181
|
+
}
|
182
|
+
end
|
183
|
+
|
151
184
|
def create_apimap
|
185
|
+
setup_forest_liana_meta
|
186
|
+
|
152
187
|
ForestLiana.models.map do |model|
|
153
188
|
if analyze_model?(model)
|
154
189
|
SchemaAdapter.new(model).perform
|
@@ -184,17 +219,6 @@ module ForestLiana
|
|
184
219
|
def format_and_validate_smart_actions
|
185
220
|
ForestLiana.apimap.each do |collection|
|
186
221
|
collection.actions.each do |action|
|
187
|
-
if action.global
|
188
|
-
FOREST_LOGGER.warn "DEPRECATION WARNING: Smart Action \"global\" option is now " \
|
189
|
-
"deprecated. Please set \"type: 'global'\" instead of \"global: true\" for the " \
|
190
|
-
"\"#{action.name}\" Smart Action."
|
191
|
-
end
|
192
|
-
|
193
|
-
if action.type && !['bulk', 'global', 'single'].include?(action.type)
|
194
|
-
FOREST_LOGGER.warn "Please set a valid Smart Action type (\"bulk\", \"global\" or " \
|
195
|
-
"\"single\") for the \"#{action.name}\" Smart Action."
|
196
|
-
end
|
197
|
-
|
198
222
|
if action.fields
|
199
223
|
# NOTICE: Set a position to the Smart Actions fields.
|
200
224
|
action.fields.each_with_index do |field, index|
|
@@ -206,18 +230,8 @@ module ForestLiana
|
|
206
230
|
end
|
207
231
|
|
208
232
|
def get_apimap_serialized
|
209
|
-
|
210
|
-
|
211
|
-
include: ['actions', 'segments'],
|
212
|
-
meta: {
|
213
|
-
liana: 'forest-rails',
|
214
|
-
liana_version: liana_version,
|
215
|
-
framework_version: Gem.loaded_specs["rails"].version.version,
|
216
|
-
orm_version: Gem.loaded_specs["activerecord"].version.version,
|
217
|
-
database_type: database_type
|
218
|
-
}
|
219
|
-
})
|
220
|
-
|
233
|
+
serializer = ForestLiana::SchemaSerializer.new(@collections_sent, @meta_sent)
|
234
|
+
apimap = serializer.serialize
|
221
235
|
ForestLiana::ApimapSorter.new(apimap).perform
|
222
236
|
end
|
223
237
|
|
@@ -287,12 +301,10 @@ module ForestLiana
|
|
287
301
|
model_name = ForestLiana.name_for(collection_name.constantize)
|
288
302
|
# TODO: Remove once lianas prior to 2.0.0 are not supported anymore.
|
289
303
|
model_name_old = ForestLiana.name_old_for(collection_name.constantize)
|
290
|
-
collection_display_name = collection_name.capitalize
|
291
304
|
|
292
305
|
ForestLiana.apimap << ForestLiana::Model::Collection.new({
|
293
306
|
name: "#{model_name}_intercom_conversations",
|
294
307
|
name_old: "#{model_name_old}_intercom_conversations",
|
295
|
-
display_name: collection_display_name + ' Conversations',
|
296
308
|
icon: 'intercom',
|
297
309
|
integration: 'intercom',
|
298
310
|
only_for_relationships: true,
|
@@ -310,30 +322,29 @@ module ForestLiana
|
|
310
322
|
ForestLiana.apimap << ForestLiana::Model::Collection.new({
|
311
323
|
name: "#{model_name}_intercom_attributes",
|
312
324
|
name_old: "#{model_name_old}_intercom_attributes",
|
313
|
-
display_name: collection_display_name + ' Attributes',
|
314
325
|
icon: 'intercom',
|
315
326
|
integration: 'intercom',
|
316
327
|
only_for_relationships: true,
|
317
328
|
is_virtual: true,
|
318
329
|
is_searchable: false,
|
319
330
|
fields: [
|
320
|
-
{ field: :created_at, type: 'Date',
|
321
|
-
{ field: :updated_at, type: 'Date',
|
322
|
-
{ field: :session_count, type: 'Number',
|
323
|
-
{ field: :last_seen_ip, type: 'String',
|
324
|
-
{ field: :signed_up_at, type: 'Date',
|
325
|
-
{ field: :country, type: 'String',
|
326
|
-
{ field: :city, type: 'String',
|
327
|
-
{ field: :browser, type: 'String',
|
328
|
-
{ field: :platform, type: 'String',
|
329
|
-
{ field: :companies, type: 'String',
|
330
|
-
{ field: :segments, type: 'String',
|
331
|
-
{ field: :tags, type: 'String',
|
331
|
+
{ field: :created_at, type: 'Date', is_filterable: false },
|
332
|
+
{ field: :updated_at, type: 'Date', is_filterable: false },
|
333
|
+
{ field: :session_count, type: 'Number', is_filterable: false },
|
334
|
+
{ field: :last_seen_ip, type: 'String', is_filterable: false },
|
335
|
+
{ field: :signed_up_at, type: 'Date', is_filterable: false },
|
336
|
+
{ field: :country, type: 'String', is_filterable: false },
|
337
|
+
{ field: :city, type: 'String', is_filterable: false },
|
338
|
+
{ field: :browser, type: 'String', is_filterable: false },
|
339
|
+
{ field: :platform, type: 'String', is_filterable: false },
|
340
|
+
{ field: :companies, type: 'String', is_filterable: false },
|
341
|
+
{ field: :segments, type: 'String', is_filterable: false },
|
342
|
+
{ field: :tags, type: 'String', is_filterable: false },
|
332
343
|
{
|
333
344
|
field: :geoloc,
|
334
345
|
type: 'String',
|
335
346
|
widget: 'map',
|
336
|
-
|
347
|
+
is_filterable: false
|
337
348
|
}
|
338
349
|
]
|
339
350
|
})
|
@@ -355,12 +366,10 @@ module ForestLiana
|
|
355
366
|
model_name = ForestLiana.name_for(collection_name.constantize)
|
356
367
|
# TODO: Remove once lianas prior to 2.0.0 are not supported anymore.
|
357
368
|
model_name_old = ForestLiana.name_old_for(collection_name.constantize)
|
358
|
-
collection_display_name = model_name.capitalize
|
359
369
|
|
360
370
|
ForestLiana.apimap << ForestLiana::Model::Collection.new({
|
361
371
|
name: "#{model_name}_stripe_payments",
|
362
372
|
name_old: "#{model_name_old}_stripe_payments",
|
363
|
-
display_name: collection_display_name + ' Payments',
|
364
373
|
icon: 'stripe',
|
365
374
|
integration: 'stripe',
|
366
375
|
is_virtual: true,
|
@@ -368,24 +377,22 @@ module ForestLiana
|
|
368
377
|
is_searchable: false,
|
369
378
|
pagination_type: 'cursor',
|
370
379
|
fields: [
|
371
|
-
{ field: :id, type: 'String',
|
372
|
-
{ field: :created, type: 'Date',
|
373
|
-
{ field: :amount, type: 'Number',
|
374
|
-
{ field: :status, type: 'String',
|
375
|
-
{ field: :currency, type: 'String',
|
376
|
-
{ field: :refunded, type: 'Boolean',
|
377
|
-
{ field: :description, type: 'String',
|
380
|
+
{ field: :id, type: 'String', is_filterable: false },
|
381
|
+
{ field: :created, type: 'Date', is_filterable: false },
|
382
|
+
{ field: :amount, type: 'Number', is_filterable: false },
|
383
|
+
{ field: :status, type: 'String', is_filterable: false },
|
384
|
+
{ field: :currency, type: 'String', is_filterable: false },
|
385
|
+
{ field: :refunded, type: 'Boolean', is_filterable: false },
|
386
|
+
{ field: :description, type: 'String', is_filterable: false },
|
378
387
|
{
|
379
388
|
field: :customer,
|
380
389
|
type: 'String',
|
381
|
-
relationship: 'BelongsTo',
|
382
390
|
reference: "#{model_name}.id",
|
383
|
-
|
391
|
+
is_filterable: false
|
384
392
|
}
|
385
393
|
],
|
386
394
|
actions: [
|
387
395
|
ForestLiana::Model::Action.new({
|
388
|
-
id: 'stripe.Refund',
|
389
396
|
name: 'Refund',
|
390
397
|
endpoint: '/forest/stripe_payments/refunds'
|
391
398
|
})
|
@@ -395,7 +402,6 @@ module ForestLiana
|
|
395
402
|
ForestLiana.apimap << ForestLiana::Model::Collection.new({
|
396
403
|
name: "#{model_name}_stripe_invoices",
|
397
404
|
name_old: "#{model_name_old}_stripe_invoices",
|
398
|
-
display_name: collection_display_name + ' Invoices',
|
399
405
|
icon: 'stripe',
|
400
406
|
integration: 'stripe',
|
401
407
|
is_virtual: true,
|
@@ -403,27 +409,26 @@ module ForestLiana
|
|
403
409
|
is_searchable: false,
|
404
410
|
pagination_type: 'cursor',
|
405
411
|
fields: [
|
406
|
-
{ field: :id, type: 'String',
|
407
|
-
{ field: :amount_due, type: 'Number',
|
408
|
-
{ field: :attempt_count, type: 'Number',
|
409
|
-
{ field: :attempted, type: 'Boolean',
|
410
|
-
{ field: :closed, type: 'Boolean',
|
411
|
-
{ field: :currency, type: 'String',
|
412
|
-
{ field: :date, type: 'Date',
|
413
|
-
{ field: :forgiven, type: 'Boolean',
|
414
|
-
{ field: :period_start, type: 'Date',
|
415
|
-
{ field: :period_end, type: 'Date',
|
416
|
-
{ field: :subtotal, type: 'Number',
|
417
|
-
{ field: :total, type: 'Number',
|
418
|
-
{ field: :application_fee, type: 'Number',
|
419
|
-
{ field: :tax, type: 'Number',
|
420
|
-
{ field: :tax_percent, type: 'Number',
|
412
|
+
{ field: :id, type: 'String', is_filterable: false },
|
413
|
+
{ field: :amount_due, type: 'Number', is_filterable: false },
|
414
|
+
{ field: :attempt_count, type: 'Number', is_filterable: false },
|
415
|
+
{ field: :attempted, type: 'Boolean', is_filterable: false },
|
416
|
+
{ field: :closed, type: 'Boolean', is_filterable: false },
|
417
|
+
{ field: :currency, type: 'String', is_filterable: false },
|
418
|
+
{ field: :date, type: 'Date', is_filterable: false },
|
419
|
+
{ field: :forgiven, type: 'Boolean', is_filterable: false },
|
420
|
+
{ field: :period_start, type: 'Date', is_filterable: false },
|
421
|
+
{ field: :period_end, type: 'Date', is_filterable: false },
|
422
|
+
{ field: :subtotal, type: 'Number', is_filterable: false },
|
423
|
+
{ field: :total, type: 'Number', is_filterable: false },
|
424
|
+
{ field: :application_fee, type: 'Number', is_filterable: false },
|
425
|
+
{ field: :tax, type: 'Number', is_filterable: false },
|
426
|
+
{ field: :tax_percent, type: 'Number', is_filterable: false },
|
421
427
|
{
|
422
428
|
field: :customer,
|
423
429
|
type: 'String',
|
424
|
-
relationship: 'BelongsTo',
|
425
430
|
reference: "#{model_name}.id",
|
426
|
-
|
431
|
+
is_filterable: false
|
427
432
|
}
|
428
433
|
]
|
429
434
|
})
|
@@ -431,7 +436,6 @@ module ForestLiana
|
|
431
436
|
ForestLiana.apimap << ForestLiana::Model::Collection.new({
|
432
437
|
name: "#{model_name}_stripe_cards",
|
433
438
|
name_old: "#{model_name_old}_stripe_cards",
|
434
|
-
display_name: collection_display_name + ' Cards',
|
435
439
|
icon: 'stripe',
|
436
440
|
integration: 'stripe',
|
437
441
|
is_virtual: true,
|
@@ -440,27 +444,26 @@ module ForestLiana
|
|
440
444
|
only_for_relationships: true,
|
441
445
|
pagination_type: 'cursor',
|
442
446
|
fields: [
|
443
|
-
{ field: :id, type: 'String',
|
444
|
-
{ field: :last4, type: 'String',
|
445
|
-
{ field: :brand, type: 'String',
|
446
|
-
{ field: :funding, type: 'String',
|
447
|
-
{ field: :exp_month, type: 'Number',
|
448
|
-
{ field: :exp_year, type: 'Number',
|
449
|
-
{ field: :country, type: 'String',
|
450
|
-
{ field: :name, type: 'String',
|
451
|
-
{ field: :address_line1, type: 'String',
|
452
|
-
{ field: :address_line2, type: 'String',
|
453
|
-
{ field: :address_city, type: 'String',
|
454
|
-
{ field: :address_state, type: 'String',
|
455
|
-
{ field: :address_zip, type: 'String',
|
456
|
-
{ field: :address_country, type: 'String',
|
457
|
-
{ field: :cvc_check, type: 'String',
|
447
|
+
{ field: :id, type: 'String', is_filterable: false },
|
448
|
+
{ field: :last4, type: 'String', is_filterable: false },
|
449
|
+
{ field: :brand, type: 'String', is_filterable: false },
|
450
|
+
{ field: :funding, type: 'String', is_filterable: false },
|
451
|
+
{ field: :exp_month, type: 'Number', is_filterable: false },
|
452
|
+
{ field: :exp_year, type: 'Number', is_filterable: false },
|
453
|
+
{ field: :country, type: 'String', is_filterable: false },
|
454
|
+
{ field: :name, type: 'String', is_filterable: false },
|
455
|
+
{ field: :address_line1, type: 'String', is_filterable: false },
|
456
|
+
{ field: :address_line2, type: 'String', is_filterable: false },
|
457
|
+
{ field: :address_city, type: 'String', is_filterable: false },
|
458
|
+
{ field: :address_state, type: 'String', is_filterable: false },
|
459
|
+
{ field: :address_zip, type: 'String', is_filterable: false },
|
460
|
+
{ field: :address_country, type: 'String', is_filterable: false },
|
461
|
+
{ field: :cvc_check, type: 'String', is_filterable: false },
|
458
462
|
{
|
459
463
|
field: :customer,
|
460
464
|
type: 'String',
|
461
|
-
relationship: 'BelongsTo',
|
462
465
|
reference: "#{model_name}.id",
|
463
|
-
|
466
|
+
is_filterable: false
|
464
467
|
}
|
465
468
|
]
|
466
469
|
})
|
@@ -468,7 +471,6 @@ module ForestLiana
|
|
468
471
|
ForestLiana.apimap << ForestLiana::Model::Collection.new({
|
469
472
|
name: "#{model_name}_stripe_subscriptions",
|
470
473
|
name_old: "#{model_name_old}_stripe_subscriptions",
|
471
|
-
display_name: collection_display_name + ' Subscriptions',
|
472
474
|
icon: 'stripe',
|
473
475
|
integration: 'stripe',
|
474
476
|
is_virtual: true,
|
@@ -476,26 +478,25 @@ module ForestLiana
|
|
476
478
|
is_searchable: false,
|
477
479
|
pagination_type: 'cursor',
|
478
480
|
fields: [
|
479
|
-
{ field: :id, type: 'String',
|
480
|
-
{ field: :cancel_at_period_end, type: 'Boolean',
|
481
|
-
{ field: :canceled_at, type: 'Date',
|
482
|
-
{ field: :created, type: 'Date',
|
483
|
-
{ field: :current_period_end, type: 'Date',
|
484
|
-
{ field: :current_period_start, type: 'Date',
|
485
|
-
{ field: :ended_at, type: 'Date',
|
486
|
-
{ field: :livemode, type: 'Boolean',
|
487
|
-
{ field: :quantity, type: 'Number',
|
488
|
-
{ field: :start, type: 'Date',
|
489
|
-
{ field: :status, type: 'String',
|
490
|
-
{ field: :tax_percent, type: 'Number',
|
491
|
-
{ field: :trial_end, type: 'Date',
|
492
|
-
{ field: :trial_start, type: 'Date',
|
481
|
+
{ field: :id, type: 'String', is_filterable: false },
|
482
|
+
{ field: :cancel_at_period_end, type: 'Boolean', is_filterable: false },
|
483
|
+
{ field: :canceled_at, type: 'Date', is_filterable: false },
|
484
|
+
{ field: :created, type: 'Date', is_filterable: false },
|
485
|
+
{ field: :current_period_end, type: 'Date', is_filterable: false },
|
486
|
+
{ field: :current_period_start, type: 'Date', is_filterable: false },
|
487
|
+
{ field: :ended_at, type: 'Date', is_filterable: false },
|
488
|
+
{ field: :livemode, type: 'Boolean', is_filterable: false },
|
489
|
+
{ field: :quantity, type: 'Number', is_filterable: false },
|
490
|
+
{ field: :start, type: 'Date', is_filterable: false },
|
491
|
+
{ field: :status, type: 'String', is_filterable: false },
|
492
|
+
{ field: :tax_percent, type: 'Number', is_filterable: false },
|
493
|
+
{ field: :trial_end, type: 'Date', is_filterable: false },
|
494
|
+
{ field: :trial_start, type: 'Date', is_filterable: false },
|
493
495
|
{
|
494
496
|
field: :customer,
|
495
497
|
type: 'String',
|
496
|
-
relationship: 'BelongsTo',
|
497
498
|
reference: "#{model_name}.id",
|
498
|
-
|
499
|
+
is_filterable: false
|
499
500
|
}
|
500
501
|
]
|
501
502
|
})
|
@@ -503,7 +504,6 @@ module ForestLiana
|
|
503
504
|
ForestLiana.apimap << ForestLiana::Model::Collection.new({
|
504
505
|
name: "#{model_name}_stripe_bank_accounts",
|
505
506
|
name_old: "#{model_name_old}_stripe_bank_accounts",
|
506
|
-
display_name: collection_display_name + ' Bank Accounts',
|
507
507
|
icon: 'stripe',
|
508
508
|
integration: 'stripe',
|
509
509
|
is_virtual: true,
|
@@ -512,24 +512,23 @@ module ForestLiana
|
|
512
512
|
only_for_relationships: true,
|
513
513
|
pagination_type: 'cursor',
|
514
514
|
fields: [
|
515
|
-
{ field: :id, type: 'String',
|
516
|
-
{ field: :account, type: 'String',
|
517
|
-
{ field: :account_holder_name, type: 'String',
|
518
|
-
{ field: :account_holder_type, type: 'String',
|
519
|
-
{ field: :bank_name, type: 'String',
|
520
|
-
{ field: :country, type: 'String',
|
521
|
-
{ field: :currency, type: 'String',
|
522
|
-
{ field: :default_for_currency, type: 'Boolean',
|
523
|
-
{ field: :fingerprint, type: 'String',
|
524
|
-
{ field: :last4, type: 'String',
|
525
|
-
{ field: :rooting_number, type: 'String',
|
526
|
-
{ field: :status, type: 'String',
|
515
|
+
{ field: :id, type: 'String', is_filterable: false },
|
516
|
+
{ field: :account, type: 'String', is_filterable: false },
|
517
|
+
{ field: :account_holder_name, type: 'String', is_filterable: false },
|
518
|
+
{ field: :account_holder_type, type: 'String', is_filterable: false },
|
519
|
+
{ field: :bank_name, type: 'String', is_filterable: false },
|
520
|
+
{ field: :country, type: 'String', is_filterable: false },
|
521
|
+
{ field: :currency, type: 'String', is_filterable: false },
|
522
|
+
{ field: :default_for_currency, type: 'Boolean', is_filterable: false },
|
523
|
+
{ field: :fingerprint, type: 'String', is_filterable: false },
|
524
|
+
{ field: :last4, type: 'String', is_filterable: false },
|
525
|
+
{ field: :rooting_number, type: 'String', is_filterable: false },
|
526
|
+
{ field: :status, type: 'String', is_filterable: false },
|
527
527
|
{
|
528
528
|
field: :customer,
|
529
529
|
type: 'String',
|
530
|
-
relationship: 'BelongsTo',
|
531
530
|
reference: "#{model_name}.id",
|
532
|
-
|
531
|
+
is_filterable: false
|
533
532
|
}
|
534
533
|
]
|
535
534
|
})
|
@@ -567,9 +566,8 @@ module ForestLiana
|
|
567
566
|
def setup_mixpanel_integration(collection_name_and_field)
|
568
567
|
collection_name = collection_name_and_field.split('.')[0]
|
569
568
|
model_name = ForestLiana.name_for(collection_name.constantize)
|
570
|
-
collection_display_name = model_name.capitalize
|
571
569
|
|
572
|
-
field_attributes = {
|
570
|
+
field_attributes = { is_filterable: false , is_virtual: true, is_sortable: false }
|
573
571
|
|
574
572
|
fields = [
|
575
573
|
{ field: :id, type: 'String' },
|
@@ -596,7 +594,6 @@ module ForestLiana
|
|
596
594
|
|
597
595
|
ForestLiana.apimap << ForestLiana::Model::Collection.new({
|
598
596
|
name: "#{model_name}_mixpanel_events",
|
599
|
-
display_name: "#{collection_display_name} Events",
|
600
597
|
icon: 'mixpanel',
|
601
598
|
integration: 'mixpanel',
|
602
599
|
is_virtual: true,
|