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.
Files changed (93) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +1 -1
  3. data/app/controllers/forest_liana/resources_controller.rb +9 -17
  4. data/app/controllers/forest_liana/stats_controller.rb +4 -14
  5. data/app/models/forest_liana/model/action.rb +57 -3
  6. data/app/models/forest_liana/model/collection.rb +37 -9
  7. data/app/models/forest_liana/model/segment.rb +1 -1
  8. data/app/serializers/forest_liana/schema_serializer.rb +83 -0
  9. data/app/serializers/forest_liana/serializer_factory.rb +13 -48
  10. data/app/services/forest_liana/apimap_sorter.rb +88 -48
  11. data/app/services/forest_liana/base_getter.rb +1 -7
  12. data/app/services/forest_liana/has_many_getter.rb +14 -15
  13. data/app/services/forest_liana/line_stat_getter.rb +5 -1
  14. data/app/services/forest_liana/operator_value_parser.rb +1 -46
  15. data/app/services/forest_liana/pie_stat_getter.rb +2 -2
  16. data/app/services/forest_liana/query_stat_getter.rb +3 -25
  17. data/app/services/forest_liana/resource_getter.rb +8 -2
  18. data/app/services/forest_liana/resources_getter.rb +13 -27
  19. data/app/services/forest_liana/schema_adapter.rb +65 -64
  20. data/app/services/forest_liana/search_query_builder.rb +24 -5
  21. data/app/services/forest_liana/stat_getter.rb +8 -1
  22. data/app/services/forest_liana/value_stat_getter.rb +2 -2
  23. data/lib/forest_liana.rb +2 -0
  24. data/lib/forest_liana/bootstraper.rb +119 -122
  25. data/lib/forest_liana/collection.rb +41 -12
  26. data/lib/forest_liana/engine.rb +16 -11
  27. data/lib/forest_liana/json_printer.rb +54 -0
  28. data/lib/forest_liana/schema_file_updater.rb +141 -0
  29. data/lib/forest_liana/version.rb +1 -1
  30. data/test/dummy/config/routes.rb +1 -0
  31. data/test/dummy/db/test.sqlite3 +0 -0
  32. data/test/dummy/log/test.log +116056 -3826
  33. data/test/integration/navigation_test.rb +10 -0
  34. data/test/routing/route_test.rb +2 -0
  35. data/test/services/forest_liana/resources_getter_test.rb +3 -3
  36. data/test/services/forest_liana/schema_adapter_test.rb +50 -49
  37. metadata +11 -111
  38. data/app/helpers/forest_liana/query_helper.rb +0 -30
  39. data/app/helpers/forest_liana/schema_helper.rb +0 -8
  40. data/app/serializers/forest_liana/action_serializer.rb +0 -21
  41. data/app/serializers/forest_liana/collection_serializer.rb +0 -33
  42. data/app/serializers/forest_liana/segment_serializer.rb +0 -15
  43. data/app/services/forest_liana/leaderboard_stat_getter.rb +0 -44
  44. data/app/services/forest_liana/objective_stat_getter.rb +0 -10
  45. data/spec/dummy/README.rdoc +0 -28
  46. data/spec/dummy/Rakefile +0 -6
  47. data/spec/dummy/app/assets/javascripts/application.js +0 -13
  48. data/spec/dummy/app/assets/stylesheets/application.css +0 -15
  49. data/spec/dummy/app/config/routes.rb +0 -3
  50. data/spec/dummy/app/controllers/application_controller.rb +0 -5
  51. data/spec/dummy/app/helpers/application_helper.rb +0 -2
  52. data/spec/dummy/app/models/island.rb +0 -5
  53. data/spec/dummy/app/models/tree.rb +0 -5
  54. data/spec/dummy/app/models/user.rb +0 -4
  55. data/spec/dummy/app/views/layouts/application.html.erb +0 -14
  56. data/spec/dummy/bin/bundle +0 -3
  57. data/spec/dummy/bin/rails +0 -4
  58. data/spec/dummy/bin/rake +0 -4
  59. data/spec/dummy/bin/setup +0 -29
  60. data/spec/dummy/config.ru +0 -4
  61. data/spec/dummy/config/application.rb +0 -26
  62. data/spec/dummy/config/boot.rb +0 -5
  63. data/spec/dummy/config/database.yml +0 -25
  64. data/spec/dummy/config/environment.rb +0 -5
  65. data/spec/dummy/config/environments/development.rb +0 -41
  66. data/spec/dummy/config/environments/production.rb +0 -79
  67. data/spec/dummy/config/environments/test.rb +0 -42
  68. data/spec/dummy/config/initializers/assets.rb +0 -11
  69. data/spec/dummy/config/initializers/backtrace_silencers.rb +0 -7
  70. data/spec/dummy/config/initializers/cookies_serializer.rb +0 -3
  71. data/spec/dummy/config/initializers/filter_parameter_logging.rb +0 -4
  72. data/spec/dummy/config/initializers/forest_liana.rb +0 -2
  73. data/spec/dummy/config/initializers/inflections.rb +0 -16
  74. data/spec/dummy/config/initializers/mime_types.rb +0 -4
  75. data/spec/dummy/config/initializers/session_store.rb +0 -3
  76. data/spec/dummy/config/initializers/wrap_parameters.rb +0 -14
  77. data/spec/dummy/config/routes.rb +0 -3
  78. data/spec/dummy/config/secrets.yml +0 -22
  79. data/spec/dummy/db/migrate/20190226172951_create_user.rb +0 -9
  80. data/spec/dummy/db/migrate/20190226173051_create_isle.rb +0 -10
  81. data/spec/dummy/db/migrate/20190226174951_create_tree.rb +0 -12
  82. data/spec/dummy/db/schema.rb +0 -42
  83. data/spec/dummy/db/test.sqlite3 +0 -0
  84. data/spec/dummy/log/development.log +0 -80
  85. data/spec/dummy/log/test.log +0 -26139
  86. data/spec/helpers/forest_liana/query_helper_spec.rb +0 -74
  87. data/spec/helpers/forest_liana/schema_helper_spec.rb +0 -13
  88. data/spec/rails_helper.rb +0 -61
  89. data/spec/requests/resources_spec.rb +0 -97
  90. data/spec/services/forest_liana/apimap_sorter_spec.rb +0 -172
  91. data/spec/services/forest_liana/ip_whitelist_checker_spec.rb +0 -203
  92. data/spec/services/forest_liana/schema_adapter_spec.rb +0 -17
  93. 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
- compute_includes()
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(@includes)
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 no search or
29
- # filters on associations.
30
- @records_count = @count_needs_includes ? @records.eager_load(@includes).count : @records.count
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 compute_includes
42
- associations_has_one = ForestLiana::QueryHelper.get_one_associations(@resource)
43
-
44
- includes = associations_has_one.map(&:name)
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
- @includes = (includes & @field_names_requested).concat(includes_for_smart_search)
57
+ (includes & @field_names_requested).concat(includes_for_smart_search)
61
58
  else
62
- @includes = includes
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
- @fields_to_serialize | associations_for_query
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
- field_schema = get_schema_for_column(column)
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
- 'is-filterable': false,
116
+ is_filterable: false,
119
117
  integration: 'intercom'
120
118
  }
121
119
 
122
- @collection.fields << {
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
- 'is-filterable': false,
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
- 'is-filterable': false,
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
- 'is-filterable': false,
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
- 'is-filterable': false,
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
- 'is-filterable': false,
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
- 'is-filterable': false,
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
- 'is-filterable': false,
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[:inverseOf] = inverse_of(association)
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
- column_type = get_type_for(column)
288
- return nil if column_type.nil?
289
-
290
- schema = { field: column.name, type: column_type }
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
- inverseOf: inverse_of(association),
304
- 'is-filterable': !is_many_association(association)
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['default-value'] = column.default if column.default
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['is-required'] = true
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
- if column_schema[:type] == 'String'
422
- validator.options.each do |option, value|
423
- case option
424
- when :minimum
425
- column_schema[:validations] << {
426
- type: 'is longer than',
427
- value: value,
428
- message: validator.options[:message]
429
- }
430
- when :maximum
431
- column_schema[:validations] << {
432
- type: 'is shorter than',
433
- value: value,
434
- message: validator.options[:message]
435
- }
436
- when :is
437
- column_schema[:validations] << {
438
- type: 'is longer than',
439
- value: value,
440
- message: validator.options[:message]
441
- }
442
- column_schema[:validations] << {
443
- type: 'is shorter than',
444
- value: value,
445
- message: validator.options[:message]
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
- ForestLiana::QueryHelper.get_one_association_names_symbol(@resource).each do |association|
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
- condition = OperatorValueParser.get_has_one_condition(@resource, field, value, @params[:timezone])
265
- @records.where(condition) if condition
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
- compute_includes()
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(@includes)
8
- valuePrevious = get_resource().eager_load(@includes)
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]
@@ -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
- apimap = JSONAPI::Serializer.serialize(ForestLiana.apimap, {
210
- is_collection: true,
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', 'is-filterable': false },
321
- { field: :updated_at, type: 'Date', 'is-filterable': false },
322
- { field: :session_count, type: 'Number', 'is-filterable': false },
323
- { field: :last_seen_ip, type: 'String', 'is-filterable': false },
324
- { field: :signed_up_at, type: 'Date', 'is-filterable': false },
325
- { field: :country, type: 'String', 'is-filterable': false },
326
- { field: :city, type: 'String', 'is-filterable': false },
327
- { field: :browser, type: 'String', 'is-filterable': false },
328
- { field: :platform, type: 'String', 'is-filterable': false },
329
- { field: :companies, type: 'String', 'is-filterable': false },
330
- { field: :segments, type: 'String', 'is-filterable': false },
331
- { field: :tags, type: 'String', 'is-filterable': false },
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
- 'is-filterable': false
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', 'is-filterable': false },
372
- { field: :created, type: 'Date', 'is-filterable': false },
373
- { field: :amount, type: 'Number', 'is-filterable': false },
374
- { field: :status, type: 'String', 'is-filterable': false },
375
- { field: :currency, type: 'String', 'is-filterable': false },
376
- { field: :refunded, type: 'Boolean', 'is-filterable': false },
377
- { field: :description, type: 'String', 'is-filterable': false },
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
- 'is-filterable': false
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', 'is-filterable': false },
407
- { field: :amount_due, type: 'Number', 'is-filterable': false },
408
- { field: :attempt_count, type: 'Number', 'is-filterable': false },
409
- { field: :attempted, type: 'Boolean', 'is-filterable': false },
410
- { field: :closed, type: 'Boolean', 'is-filterable': false },
411
- { field: :currency, type: 'String', 'is-filterable': false },
412
- { field: :date, type: 'Date', 'is-filterable': false },
413
- { field: :forgiven, type: 'Boolean', 'is-filterable': false },
414
- { field: :period_start, type: 'Date', 'is-filterable': false },
415
- { field: :period_end, type: 'Date', 'is-filterable': false },
416
- { field: :subtotal, type: 'Number', 'is-filterable': false },
417
- { field: :total, type: 'Number', 'is-filterable': false },
418
- { field: :application_fee, type: 'Number', 'is-filterable': false },
419
- { field: :tax, type: 'Number', 'is-filterable': false },
420
- { field: :tax_percent, type: 'Number', 'is-filterable': false },
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
- 'is-filterable': false
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', 'is-filterable': false },
444
- { field: :last4, type: 'String', 'is-filterable': false },
445
- { field: :brand, type: 'String', 'is-filterable': false },
446
- { field: :funding, type: 'String', 'is-filterable': false },
447
- { field: :exp_month, type: 'Number', 'is-filterable': false },
448
- { field: :exp_year, type: 'Number', 'is-filterable': false },
449
- { field: :country, type: 'String', 'is-filterable': false },
450
- { field: :name, type: 'String', 'is-filterable': false },
451
- { field: :address_line1, type: 'String', 'is-filterable': false },
452
- { field: :address_line2, type: 'String', 'is-filterable': false },
453
- { field: :address_city, type: 'String', 'is-filterable': false },
454
- { field: :address_state, type: 'String', 'is-filterable': false },
455
- { field: :address_zip, type: 'String', 'is-filterable': false },
456
- { field: :address_country, type: 'String', 'is-filterable': false },
457
- { field: :cvc_check, type: 'String', 'is-filterable': false },
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
- 'is-filterable': false
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', 'is-filterable': false },
480
- { field: :cancel_at_period_end, type: 'Boolean', 'is-filterable': false },
481
- { field: :canceled_at, type: 'Date', 'is-filterable': false },
482
- { field: :created, type: 'Date', 'is-filterable': false },
483
- { field: :current_period_end, type: 'Date', 'is-filterable': false },
484
- { field: :current_period_start, type: 'Date', 'is-filterable': false },
485
- { field: :ended_at, type: 'Date', 'is-filterable': false },
486
- { field: :livemode, type: 'Boolean', 'is-filterable': false },
487
- { field: :quantity, type: 'Number', 'is-filterable': false },
488
- { field: :start, type: 'Date', 'is-filterable': false },
489
- { field: :status, type: 'String', 'is-filterable': false },
490
- { field: :tax_percent, type: 'Number', 'is-filterable': false },
491
- { field: :trial_end, type: 'Date', 'is-filterable': false },
492
- { field: :trial_start, type: 'Date', 'is-filterable': false },
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
- 'is-filterable': false
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', '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 },
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
- 'is-filterable': false
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 = { 'is-filterable': false , 'is-virtual': true, 'is-sortable': false }
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,