From de72c6e514ebb3483ffaf0fbc739acfeae0a56e7 Mon Sep 17 00:00:00 2001 From: Sergio Marques Date: Thu, 16 Mar 2023 13:11:24 +0000 Subject: [PATCH 01/36] fix: schema version generation for all versions (#613) --- lib/forest_liana/bootstrapper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/forest_liana/bootstrapper.rb b/lib/forest_liana/bootstrapper.rb index ee50ab23..0b0a45ed 100644 --- a/lib/forest_liana/bootstrapper.rb +++ b/lib/forest_liana/bootstrapper.rb @@ -202,7 +202,7 @@ def namespace_duplicated_models def setup_forest_liana_meta ForestLiana.meta = { liana: 'forest-rails', - liana_version: ForestLiana::VERSION.sub!('.beta', '-beta'), + liana_version: ForestLiana::VERSION.sub('.beta', '-beta'), stack: { database_type: database_type, orm_version: Gem.loaded_specs["activerecord"].version.version, From af6842264de5f0c3eae64140282f2050d9135c93 Mon Sep 17 00:00:00 2001 From: Forest Bot Date: Thu, 16 Mar 2023 13:13:05 +0000 Subject: [PATCH 02/36] chore(release): 8.0.1 [skip ci] ## [8.0.1](https://github.com/ForestAdmin/forest-rails/compare/v8.0.0...v8.0.1) (2023-03-16) ### Bug Fixes * schema version generation for all versions ([#613](https://github.com/ForestAdmin/forest-rails/issues/613)) ([de72c6e](https://github.com/ForestAdmin/forest-rails/commit/de72c6e514ebb3483ffaf0fbc739acfeae0a56e7)) --- CHANGELOG.md | 7 +++++++ Gemfile.lock | 2 +- lib/forest_liana/version.rb | 2 +- package.json | 2 +- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 02c4fb01..6c5969e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [8.0.1](https://github.com/ForestAdmin/forest-rails/compare/v8.0.0...v8.0.1) (2023-03-16) + + +### Bug Fixes + +* schema version generation for all versions ([#613](https://github.com/ForestAdmin/forest-rails/issues/613)) ([de72c6e](https://github.com/ForestAdmin/forest-rails/commit/de72c6e514ebb3483ffaf0fbc739acfeae0a56e7)) + # [8.0.0](https://github.com/ForestAdmin/forest-rails/compare/v7.8.1...v8.0.0) (2023-03-14) diff --git a/Gemfile.lock b/Gemfile.lock index c8d6a658..c82d49a4 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - forest_liana (8.0.0) + forest_liana (8.0.1) arel-helpers bcrypt deepsort diff --git a/lib/forest_liana/version.rb b/lib/forest_liana/version.rb index 353add2c..0186aa05 100644 --- a/lib/forest_liana/version.rb +++ b/lib/forest_liana/version.rb @@ -1,3 +1,3 @@ module ForestLiana - VERSION = "8.0.0" + VERSION = "8.0.1" end diff --git a/package.json b/package.json index 462d9309..4f2afd05 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "forest-rails", - "version": "8.0.0", + "version": "8.0.1", "description": "The official Rails liana for Forest.", "directories": { "test": "test" From 97e1f06d9442ed8faf8b22504caf76c8a4b50fe1 Mon Sep 17 00:00:00 2001 From: Matthieu Date: Wed, 22 Mar 2023 15:11:59 +0100 Subject: [PATCH 03/36] fix: developers can disable automatic schema send (#614) --- lib/forest_liana/engine.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/forest_liana/engine.rb b/lib/forest_liana/engine.rb index 1e687c99..e9d940bf 100644 --- a/lib/forest_liana/engine.rb +++ b/lib/forest_liana/engine.rb @@ -107,7 +107,7 @@ def eager_load_active_record_descendants app if ENV['FOREST_DEACTIVATE_AUTOMATIC_APIMAP'] FOREST_LOGGER.warn "DEPRECATION WARNING: FOREST_DEACTIVATE_AUTOMATIC_APIMAP option has been renamed. Please use FOREST_DISABLE_AUTO_SCHEMA_APPLY instead." end - bootstrapper.synchronize unless ENV['FOREST_DEACTIVATE_AUTOMATIC_APIMAP'] || ENV['FOREST_DISABLE_AUTO_SCHEMA_APPLY'] || Rails.env.test? + bootstrapper.synchronize unless ENV['FOREST_DEACTIVATE_AUTOMATIC_APIMAP'] == true || ENV['FOREST_DISABLE_AUTO_SCHEMA_APPLY'] == true || Rails.env.test? end end end From 780958b8b8923bfd909fd15d39120a76f17c084f Mon Sep 17 00:00:00 2001 From: Forest Bot Date: Wed, 22 Mar 2023 14:13:42 +0000 Subject: [PATCH 04/36] chore(release): 8.0.2 [skip ci] ## [8.0.2](https://github.com/ForestAdmin/forest-rails/compare/v8.0.1...v8.0.2) (2023-03-22) ### Bug Fixes * developers can disable automatic schema send ([#614](https://github.com/ForestAdmin/forest-rails/issues/614)) ([97e1f06](https://github.com/ForestAdmin/forest-rails/commit/97e1f06d9442ed8faf8b22504caf76c8a4b50fe1)) --- CHANGELOG.md | 7 +++++++ Gemfile.lock | 2 +- lib/forest_liana/version.rb | 2 +- package.json | 2 +- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c5969e3..49a9057b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [8.0.2](https://github.com/ForestAdmin/forest-rails/compare/v8.0.1...v8.0.2) (2023-03-22) + + +### Bug Fixes + +* developers can disable automatic schema send ([#614](https://github.com/ForestAdmin/forest-rails/issues/614)) ([97e1f06](https://github.com/ForestAdmin/forest-rails/commit/97e1f06d9442ed8faf8b22504caf76c8a4b50fe1)) + ## [8.0.1](https://github.com/ForestAdmin/forest-rails/compare/v8.0.0...v8.0.1) (2023-03-16) diff --git a/Gemfile.lock b/Gemfile.lock index c82d49a4..fb2fadac 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - forest_liana (8.0.1) + forest_liana (8.0.2) arel-helpers bcrypt deepsort diff --git a/lib/forest_liana/version.rb b/lib/forest_liana/version.rb index 0186aa05..e234fee7 100644 --- a/lib/forest_liana/version.rb +++ b/lib/forest_liana/version.rb @@ -1,3 +1,3 @@ module ForestLiana - VERSION = "8.0.1" + VERSION = "8.0.2" end diff --git a/package.json b/package.json index 4f2afd05..7b180c76 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "forest-rails", - "version": "8.0.1", + "version": "8.0.2", "description": "The official Rails liana for Forest.", "directories": { "test": "test" From a35646f4c39cb151aed14e52e1be1099b1946fd9 Mon Sep 17 00:00:00 2001 From: NicolasAlexandre Date: Thu, 6 Apr 2023 16:52:42 +0200 Subject: [PATCH 05/36] fix(permissions): use forest collection name for check the permission (#616) --- app/services/forest_liana/ability/permission.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/services/forest_liana/ability/permission.rb b/app/services/forest_liana/ability/permission.rb index 650a345a..684e76cf 100644 --- a/app/services/forest_liana/ability/permission.rb +++ b/app/services/forest_liana/ability/permission.rb @@ -13,13 +13,14 @@ def is_crud_authorized?(action, user, collection) user_data = get_user_data(user['id']) collections_data = get_collections_permissions_data + collection_name = ForestLiana.name_for(collection) begin - is_allowed = collections_data[collection.name][action].include? user_data['roleId'] + is_allowed = collections_data[collection_name][action].include? user_data['roleId'] # re-fetch if user permission is not allowed (may have been changed) unless is_allowed collections_data = get_collections_permissions_data(true) - is_allowed = collections_data[collection.name][action].include? user_data['roleId'] + is_allowed = collections_data[collection_name][action].include? user_data['roleId'] end is_allowed From 50b989f41b669647ed7112bda5e0e725aa1cf638 Mon Sep 17 00:00:00 2001 From: Forest Bot Date: Thu, 6 Apr 2023 14:54:35 +0000 Subject: [PATCH 06/36] chore(release): 8.0.3 [skip ci] ## [8.0.3](https://github.com/ForestAdmin/forest-rails/compare/v8.0.2...v8.0.3) (2023-04-06) ### Bug Fixes * **permissions:** use forest collection name for check the permission ([#616](https://github.com/ForestAdmin/forest-rails/issues/616)) ([a35646f](https://github.com/ForestAdmin/forest-rails/commit/a35646f4c39cb151aed14e52e1be1099b1946fd9)) --- CHANGELOG.md | 7 +++++++ Gemfile.lock | 2 +- lib/forest_liana/version.rb | 2 +- package.json | 2 +- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 49a9057b..bd5f8eac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [8.0.3](https://github.com/ForestAdmin/forest-rails/compare/v8.0.2...v8.0.3) (2023-04-06) + + +### Bug Fixes + +* **permissions:** use forest collection name for check the permission ([#616](https://github.com/ForestAdmin/forest-rails/issues/616)) ([a35646f](https://github.com/ForestAdmin/forest-rails/commit/a35646f4c39cb151aed14e52e1be1099b1946fd9)) + ## [8.0.2](https://github.com/ForestAdmin/forest-rails/compare/v8.0.1...v8.0.2) (2023-03-22) diff --git a/Gemfile.lock b/Gemfile.lock index fb2fadac..1ab06885 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - forest_liana (8.0.2) + forest_liana (8.0.3) arel-helpers bcrypt deepsort diff --git a/lib/forest_liana/version.rb b/lib/forest_liana/version.rb index e234fee7..8c3e07e3 100644 --- a/lib/forest_liana/version.rb +++ b/lib/forest_liana/version.rb @@ -1,3 +1,3 @@ module ForestLiana - VERSION = "8.0.2" + VERSION = "8.0.3" end diff --git a/package.json b/package.json index 7b180c76..d827b785 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "forest-rails", - "version": "8.0.2", + "version": "8.0.3", "description": "The official Rails liana for Forest.", "directories": { "test": "test" From 72bec24fee8b0397c80a93654d28f52d9c20cc15 Mon Sep 17 00:00:00 2001 From: NicolasAlexandre Date: Fri, 21 Apr 2023 15:42:08 +0200 Subject: [PATCH 07/36] fix(action): authorize all actions on development environment (#617) --- .../forest_liana/ability/permission.rb | 2 ++ spec/requests/actions_controller_spec.rb | 33 +++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/app/services/forest_liana/ability/permission.rb b/app/services/forest_liana/ability/permission.rb index 684e76cf..65b2c7d6 100644 --- a/app/services/forest_liana/ability/permission.rb +++ b/app/services/forest_liana/ability/permission.rb @@ -30,6 +30,8 @@ def is_crud_authorized?(action, user, collection) end def is_smart_action_authorized?(user, collection, parameters, endpoint, http_method) + return true unless has_permission_system? + user_data = get_user_data(user['id']) collections_data = get_collections_permissions_data begin diff --git a/spec/requests/actions_controller_spec.rb b/spec/requests/actions_controller_spec.rb index 11979bb5..6c43bee0 100644 --- a/spec/requests/actions_controller_spec.rb +++ b/spec/requests/actions_controller_spec.rb @@ -312,6 +312,39 @@ end end + describe 'calling the action on development environment' do + let(:all_records) { false } + let(:params) { + { + data: { + attributes: { + collection_name: 'Island', + ids: ['1'], + all_records: all_records, + smart_action_id: 'Island-Test' + }, + type: 'custom-action-requests' + }, + timezone: 'Europe/Paris' + } + } + + it 'should respond 200 and perform the action' do + Rails.cache.delete('forest.has_permission') + Rails.cache.delete('forest.users') + Rails.cache.write('forest.users', {'1' => { 'id' => 1, 'roleId' => 2, 'rendering_id' => '1' }}) + allow_any_instance_of(ForestLiana::Ability::Fetch) + .to receive(:get_permissions) + .with('/liana/v4/permissions/environment') + .and_return(true) + + post '/forest/actions/test', params: JSON.dump(params), headers: headers + + expect(response.status).to eq(200) + expect(JSON.parse(response.body)).to eq({'success' => 'You are OK.'}) + end + end + describe 'calling the action' do before(:each) do allow_any_instance_of(ForestLiana::Ability).to receive(:forest_authorize!) { true } From 709c41f9598efcc3ef7081b99676a66f6c846ffd Mon Sep 17 00:00:00 2001 From: Forest Bot Date: Fri, 21 Apr 2023 13:43:59 +0000 Subject: [PATCH 08/36] chore(release): 8.0.4 [skip ci] ## [8.0.4](https://github.com/ForestAdmin/forest-rails/compare/v8.0.3...v8.0.4) (2023-04-21) ### Bug Fixes * **action:** authorize all actions on development environment ([#617](https://github.com/ForestAdmin/forest-rails/issues/617)) ([72bec24](https://github.com/ForestAdmin/forest-rails/commit/72bec24fee8b0397c80a93654d28f52d9c20cc15)) --- CHANGELOG.md | 7 +++++++ Gemfile.lock | 2 +- lib/forest_liana/version.rb | 2 +- package.json | 2 +- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bd5f8eac..e0211579 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [8.0.4](https://github.com/ForestAdmin/forest-rails/compare/v8.0.3...v8.0.4) (2023-04-21) + + +### Bug Fixes + +* **action:** authorize all actions on development environment ([#617](https://github.com/ForestAdmin/forest-rails/issues/617)) ([72bec24](https://github.com/ForestAdmin/forest-rails/commit/72bec24fee8b0397c80a93654d28f52d9c20cc15)) + ## [8.0.3](https://github.com/ForestAdmin/forest-rails/compare/v8.0.2...v8.0.3) (2023-04-06) diff --git a/Gemfile.lock b/Gemfile.lock index 1ab06885..17dd673f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - forest_liana (8.0.3) + forest_liana (8.0.4) arel-helpers bcrypt deepsort diff --git a/lib/forest_liana/version.rb b/lib/forest_liana/version.rb index 8c3e07e3..c45d3ebf 100644 --- a/lib/forest_liana/version.rb +++ b/lib/forest_liana/version.rb @@ -1,3 +1,3 @@ module ForestLiana - VERSION = "8.0.3" + VERSION = "8.0.4" end diff --git a/package.json b/package.json index d827b785..39886732 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "forest-rails", - "version": "8.0.3", + "version": "8.0.4", "description": "The official Rails liana for Forest.", "directories": { "test": "test" From 861d76f2606f66d81df0cd31581950744a4b67a9 Mon Sep 17 00:00:00 2001 From: NicolasAlexandre Date: Thu, 4 May 2023 14:28:41 +0200 Subject: [PATCH 09/36] fix(relation): fix dissociate all records of relationship (#618) --- app/controllers/forest_liana/associations_controller.rb | 2 +- app/services/forest_liana/has_many_dissociator.rb | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/controllers/forest_liana/associations_controller.rb b/app/controllers/forest_liana/associations_controller.rb index d3f89cfc..2f171e87 100644 --- a/app/controllers/forest_liana/associations_controller.rb +++ b/app/controllers/forest_liana/associations_controller.rb @@ -72,7 +72,7 @@ def associate def dissociate begin - dissociator = HasManyDissociator.new(@resource, @association, params) + dissociator = HasManyDissociator.new(@resource, @association, params, forest_user) dissociator.perform head :no_content diff --git a/app/services/forest_liana/has_many_dissociator.rb b/app/services/forest_liana/has_many_dissociator.rb index 8d7cb102..b2412790 100644 --- a/app/services/forest_liana/has_many_dissociator.rb +++ b/app/services/forest_liana/has_many_dissociator.rb @@ -1,11 +1,12 @@ module ForestLiana - class HasManyDissociator < ForestLiana::ApplicationController - def initialize(resource, association, params) + class HasManyDissociator + def initialize(resource, association, params, forest_user) @resource = resource @association = association @params = params @with_deletion = @params[:delete].to_s == 'true' @data = params['data'] + @forest_user = forest_user end def perform @@ -13,11 +14,10 @@ def perform associated_records = @resource.find(@params[:id]).send(@association.name) remove_association = !@with_deletion || @association.macro == :has_and_belongs_to_many - if @data.is_a?(Array) record_ids = @data.map { |record| record[:id] } elsif @data.dig('attributes').present? - record_ids = ForestLiana::ResourcesGetter.get_ids_from_request(@params, forest_user) + record_ids = ForestLiana::ResourcesGetter.get_ids_from_request(@params, @forest_user) else record_ids = Array.new end From a84936ce849d910826f1535efa1eb8ad29604f2e Mon Sep 17 00:00:00 2001 From: Forest Bot Date: Thu, 4 May 2023 12:30:36 +0000 Subject: [PATCH 10/36] chore(release): 8.0.5 [skip ci] ## [8.0.5](https://github.com/ForestAdmin/forest-rails/compare/v8.0.4...v8.0.5) (2023-05-04) ### Bug Fixes * **relation:** fix dissociate all records of relationship ([#618](https://github.com/ForestAdmin/forest-rails/issues/618)) ([861d76f](https://github.com/ForestAdmin/forest-rails/commit/861d76f2606f66d81df0cd31581950744a4b67a9)) --- CHANGELOG.md | 7 +++++++ Gemfile.lock | 2 +- lib/forest_liana/version.rb | 2 +- package.json | 2 +- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e0211579..c4381bfa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [8.0.5](https://github.com/ForestAdmin/forest-rails/compare/v8.0.4...v8.0.5) (2023-05-04) + + +### Bug Fixes + +* **relation:** fix dissociate all records of relationship ([#618](https://github.com/ForestAdmin/forest-rails/issues/618)) ([861d76f](https://github.com/ForestAdmin/forest-rails/commit/861d76f2606f66d81df0cd31581950744a4b67a9)) + ## [8.0.4](https://github.com/ForestAdmin/forest-rails/compare/v8.0.3...v8.0.4) (2023-04-21) diff --git a/Gemfile.lock b/Gemfile.lock index 17dd673f..2cb58261 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - forest_liana (8.0.4) + forest_liana (8.0.5) arel-helpers bcrypt deepsort diff --git a/lib/forest_liana/version.rb b/lib/forest_liana/version.rb index c45d3ebf..eb986a66 100644 --- a/lib/forest_liana/version.rb +++ b/lib/forest_liana/version.rb @@ -1,3 +1,3 @@ module ForestLiana - VERSION = "8.0.4" + VERSION = "8.0.5" end diff --git a/package.json b/package.json index 39886732..7a5f7def 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "forest-rails", - "version": "8.0.4", + "version": "8.0.5", "description": "The official Rails liana for Forest.", "directories": { "test": "test" From 3e4e7d64dcbe22f981ef21d82076765fd8a12ed3 Mon Sep 17 00:00:00 2001 From: SteveBunlon Date: Fri, 5 May 2023 16:33:11 +0200 Subject: [PATCH 11/36] fix(hooks): use exact namespace to prevent controller conflict (#620) --- app/controllers/forest_liana/actions_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/forest_liana/actions_controller.rb b/app/controllers/forest_liana/actions_controller.rb index e8551abc..2b3fdb1c 100644 --- a/app/controllers/forest_liana/actions_controller.rb +++ b/app/controllers/forest_liana/actions_controller.rb @@ -1,5 +1,5 @@ module ForestLiana - class ActionsController < ApplicationController + class ActionsController < ForestLiana::ApplicationController def get_smart_action_hook_request if params[:data] && params[:data][:attributes] && params[:data][:attributes][:collection_name] From ed2a5d7add4d97bea5cf482df39d48d871358ff6 Mon Sep 17 00:00:00 2001 From: Forest Bot Date: Fri, 5 May 2023 14:35:03 +0000 Subject: [PATCH 12/36] chore(release): 8.0.6 [skip ci] ## [8.0.6](https://github.com/ForestAdmin/forest-rails/compare/v8.0.5...v8.0.6) (2023-05-05) ### Bug Fixes * **hooks:** use exact namespace to prevent controller conflict ([#620](https://github.com/ForestAdmin/forest-rails/issues/620)) ([3e4e7d6](https://github.com/ForestAdmin/forest-rails/commit/3e4e7d64dcbe22f981ef21d82076765fd8a12ed3)) --- CHANGELOG.md | 7 +++++++ Gemfile.lock | 2 +- lib/forest_liana/version.rb | 2 +- package.json | 2 +- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c4381bfa..65454ada 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [8.0.6](https://github.com/ForestAdmin/forest-rails/compare/v8.0.5...v8.0.6) (2023-05-05) + + +### Bug Fixes + +* **hooks:** use exact namespace to prevent controller conflict ([#620](https://github.com/ForestAdmin/forest-rails/issues/620)) ([3e4e7d6](https://github.com/ForestAdmin/forest-rails/commit/3e4e7d64dcbe22f981ef21d82076765fd8a12ed3)) + ## [8.0.5](https://github.com/ForestAdmin/forest-rails/compare/v8.0.4...v8.0.5) (2023-05-04) diff --git a/Gemfile.lock b/Gemfile.lock index 2cb58261..6c547ef6 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - forest_liana (8.0.5) + forest_liana (8.0.6) arel-helpers bcrypt deepsort diff --git a/lib/forest_liana/version.rb b/lib/forest_liana/version.rb index eb986a66..5f53549d 100644 --- a/lib/forest_liana/version.rb +++ b/lib/forest_liana/version.rb @@ -1,3 +1,3 @@ module ForestLiana - VERSION = "8.0.5" + VERSION = "8.0.6" end diff --git a/package.json b/package.json index 7a5f7def..0c56ebd3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "forest-rails", - "version": "8.0.5", + "version": "8.0.6", "description": "The official Rails liana for Forest.", "directories": { "test": "test" From 776f23d4afe47a0d41ca65abf9762e1c86504b3e Mon Sep 17 00:00:00 2001 From: NicolasAlexandre Date: Tue, 23 May 2023 10:02:23 +0200 Subject: [PATCH 13/36] fix(operator): replace the hard-coded duration by the duration variable (#621) --- Gemfile | 1 + Gemfile.lock | 2 ++ .../operator_date_interval_parser.rb | 6 ++++-- .../forest_liana/filters_parser_spec.rb | 19 +++++++++++++++++++ 4 files changed, 26 insertions(+), 2 deletions(-) diff --git a/Gemfile b/Gemfile index fec6372a..f391036e 100644 --- a/Gemfile +++ b/Gemfile @@ -14,6 +14,7 @@ gemspec group :development, :test do gem 'byebug' gem 'rspec-rails' + gem "timecop" end group :test do diff --git a/Gemfile.lock b/Gemfile.lock index 6c547ef6..d9f25b83 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -231,6 +231,7 @@ GEM attr_required (>= 0.0.5) httpclient (>= 2.4) thor (1.2.1) + timecop (0.9.6) timeout (0.3.1) tzinfo (2.0.5) concurrent-ruby (~> 1.0) @@ -272,6 +273,7 @@ DEPENDENCIES rspec-rails simplecov (~> 0.17.0) sqlite3 (~> 1.4) + timecop useragent BUNDLED WITH diff --git a/app/services/forest_liana/operator_date_interval_parser.rb b/app/services/forest_liana/operator_date_interval_parser.rb index 8ee9ab44..158276e6 100644 --- a/app/services/forest_liana/operator_date_interval_parser.rb +++ b/app/services/forest_liana/operator_date_interval_parser.rb @@ -114,9 +114,10 @@ def get_date_filter(operator, value) else from = to_client_timezone(duration.send(period).ago .send("beginning_of_#{period_of_time}")) - to = to_client_timezone(1.send(period).ago + to = to_client_timezone(duration.send(period).ago .send("end_of_#{period_of_time}")) end + "BETWEEN '#{from}' AND '#{to}'" end @@ -151,9 +152,10 @@ def get_date_filter_for_previous_interval(operator, value) else from = to_client_timezone((duration * 2).send(period).ago .send("beginning_of_#{period_of_time}")) - to = to_client_timezone((1 + duration).send(period).ago + to = to_client_timezone((duration * 2).send(period).ago .send("end_of_#{period_of_time}")) end + "BETWEEN '#{from}' AND '#{to}'" end diff --git a/spec/services/forest_liana/filters_parser_spec.rb b/spec/services/forest_liana/filters_parser_spec.rb index 80778ebc..46596e0d 100644 --- a/spec/services/forest_liana/filters_parser_spec.rb +++ b/spec/services/forest_liana/filters_parser_spec.rb @@ -1,4 +1,6 @@ module ForestLiana + include ActiveSupport::Testing::TimeHelpers + describe FiltersParser do let(:timezone) { 'Europe/Paris' } let(:resource) { Tree } @@ -488,5 +490,22 @@ module ForestLiana it { expect(filter_parser.apply_filters_on_previous_interval(date_condition_3).count).to eq 1 } end + + describe 'parse_condition with time operator' do + let(:freeze_time) { Time.utc(2022, 5, 22, 0, 0, 0) } + before do + Timecop.freeze freeze_time + end + + after do + Timecop.return + end + + it 'parse_condition should return the correct query interval' do + res = filter_parser.parse_condition({ 'field' => 'created_at', 'operator' => 'previous_quarter', 'value' => nil }) + + expect(res[res.index('BETWEEN')..-1]).to eq "BETWEEN '2021-12-31 22:00:00 UTC' AND '2022-03-31 21:59:59 UTC'" + end + end end end From 9191c54d3b5b800d13f5f6f604f3ddd901eabe0d Mon Sep 17 00:00:00 2001 From: Forest Bot Date: Tue, 23 May 2023 08:04:19 +0000 Subject: [PATCH 14/36] chore(release): 8.0.7 [skip ci] ## [8.0.7](https://github.com/ForestAdmin/forest-rails/compare/v8.0.6...v8.0.7) (2023-05-23) ### Bug Fixes * **operator:** replace the hard-coded duration by the duration variable ([#621](https://github.com/ForestAdmin/forest-rails/issues/621)) ([776f23d](https://github.com/ForestAdmin/forest-rails/commit/776f23d4afe47a0d41ca65abf9762e1c86504b3e)) --- CHANGELOG.md | 7 +++++++ Gemfile.lock | 2 +- lib/forest_liana/version.rb | 2 +- package.json | 2 +- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 65454ada..2044b4e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [8.0.7](https://github.com/ForestAdmin/forest-rails/compare/v8.0.6...v8.0.7) (2023-05-23) + + +### Bug Fixes + +* **operator:** replace the hard-coded duration by the duration variable ([#621](https://github.com/ForestAdmin/forest-rails/issues/621)) ([776f23d](https://github.com/ForestAdmin/forest-rails/commit/776f23d4afe47a0d41ca65abf9762e1c86504b3e)) + ## [8.0.6](https://github.com/ForestAdmin/forest-rails/compare/v8.0.5...v8.0.6) (2023-05-05) diff --git a/Gemfile.lock b/Gemfile.lock index d9f25b83..ede54b57 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - forest_liana (8.0.6) + forest_liana (8.0.7) arel-helpers bcrypt deepsort diff --git a/lib/forest_liana/version.rb b/lib/forest_liana/version.rb index 5f53549d..2f0a7054 100644 --- a/lib/forest_liana/version.rb +++ b/lib/forest_liana/version.rb @@ -1,3 +1,3 @@ module ForestLiana - VERSION = "8.0.6" + VERSION = "8.0.7" end diff --git a/package.json b/package.json index 0c56ebd3..5220715f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "forest-rails", - "version": "8.0.6", + "version": "8.0.7", "description": "The official Rails liana for Forest.", "directories": { "test": "test" From b0e01966158a37cadb2de7bf6ac177e53912d437 Mon Sep 17 00:00:00 2001 From: NicolasAlexandre Date: Tue, 23 May 2023 16:16:23 +0200 Subject: [PATCH 15/36] fix(scope): cast filters to json before merge with scope (#622) --- app/services/forest_liana/scope_manager.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/app/services/forest_liana/scope_manager.rb b/app/services/forest_liana/scope_manager.rb index b2bea64f..3f61605b 100644 --- a/app/services/forest_liana/scope_manager.rb +++ b/app/services/forest_liana/scope_manager.rb @@ -13,6 +13,7 @@ def self.apply_scopes_on_records(records, forest_user, collection_name, timezone end def self.append_scope_for_user(existing_filter, user, collection_name) + existing_filter = existing_filter.to_json if existing_filter.is_a?(ActionController::Parameters) scope_filter = get_scope_for_user(user, collection_name, as_string: true) filters = [existing_filter, scope_filter].compact From 22c12d3a14c0589407ea07edc5e81589fe1ef989 Mon Sep 17 00:00:00 2001 From: Forest Bot Date: Tue, 23 May 2023 14:18:00 +0000 Subject: [PATCH 16/36] chore(release): 8.0.8 [skip ci] ## [8.0.8](https://github.com/ForestAdmin/forest-rails/compare/v8.0.7...v8.0.8) (2023-05-23) ### Bug Fixes * **scope:** cast filters to json before merge with scope ([#622](https://github.com/ForestAdmin/forest-rails/issues/622)) ([b0e0196](https://github.com/ForestAdmin/forest-rails/commit/b0e01966158a37cadb2de7bf6ac177e53912d437)) --- CHANGELOG.md | 7 +++++++ Gemfile.lock | 2 +- lib/forest_liana/version.rb | 2 +- package.json | 2 +- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2044b4e1..9d8c7852 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [8.0.8](https://github.com/ForestAdmin/forest-rails/compare/v8.0.7...v8.0.8) (2023-05-23) + + +### Bug Fixes + +* **scope:** cast filters to json before merge with scope ([#622](https://github.com/ForestAdmin/forest-rails/issues/622)) ([b0e0196](https://github.com/ForestAdmin/forest-rails/commit/b0e01966158a37cadb2de7bf6ac177e53912d437)) + ## [8.0.7](https://github.com/ForestAdmin/forest-rails/compare/v8.0.6...v8.0.7) (2023-05-23) diff --git a/Gemfile.lock b/Gemfile.lock index ede54b57..be0110da 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - forest_liana (8.0.7) + forest_liana (8.0.8) arel-helpers bcrypt deepsort diff --git a/lib/forest_liana/version.rb b/lib/forest_liana/version.rb index 2f0a7054..5b2f4103 100644 --- a/lib/forest_liana/version.rb +++ b/lib/forest_liana/version.rb @@ -1,3 +1,3 @@ module ForestLiana - VERSION = "8.0.7" + VERSION = "8.0.8" end diff --git a/package.json b/package.json index 5220715f..7d2352ad 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "forest-rails", - "version": "8.0.7", + "version": "8.0.8", "description": "The official Rails liana for Forest.", "directories": { "test": "test" From e8967631d93a8375de26f7707f8acba93f6ecc5c Mon Sep 17 00:00:00 2001 From: SteveBunlon Date: Tue, 13 Jun 2023 15:06:03 +0000 Subject: [PATCH 17/36] fix(permissions): use correct collection name for model under module (#624) --- app/services/forest_liana/ability/permission.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/services/forest_liana/ability/permission.rb b/app/services/forest_liana/ability/permission.rb index 65b2c7d6..d13a46bd 100644 --- a/app/services/forest_liana/ability/permission.rb +++ b/app/services/forest_liana/ability/permission.rb @@ -35,9 +35,9 @@ def is_smart_action_authorized?(user, collection, parameters, endpoint, http_met user_data = get_user_data(user['id']) collections_data = get_collections_permissions_data begin - action = find_action_from_endpoint(collection.name, endpoint, http_method).name + action = find_action_from_endpoint(ForestLiana.name_for(collection), endpoint, http_method).name - smart_action_approval = SmartActionChecker.new(parameters, collection, collections_data[collection.name][:actions][action], user_data) + smart_action_approval = SmartActionChecker.new(parameters, collection, collections_data[ForestLiana.name_for(collection)][:actions][action], user_data) smart_action_approval.can_execute? rescue raise ForestLiana::Errors::ExpectedError.new(409, :conflict, "The collection #{collection} doesn't exist", 'collection not found') From 0cde34e1139808c3e21ceaeb4052733a8588c0a5 Mon Sep 17 00:00:00 2001 From: Forest Bot Date: Tue, 13 Jun 2023 15:08:24 +0000 Subject: [PATCH 18/36] chore(release): 8.0.9 [skip ci] ## [8.0.9](https://github.com/ForestAdmin/forest-rails/compare/v8.0.8...v8.0.9) (2023-06-13) ### Bug Fixes * **permissions:** use correct collection name for model under module ([#624](https://github.com/ForestAdmin/forest-rails/issues/624)) ([e896763](https://github.com/ForestAdmin/forest-rails/commit/e8967631d93a8375de26f7707f8acba93f6ecc5c)) --- CHANGELOG.md | 7 +++++++ Gemfile.lock | 2 +- lib/forest_liana/version.rb | 2 +- package.json | 2 +- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d8c7852..cbc1b5b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [8.0.9](https://github.com/ForestAdmin/forest-rails/compare/v8.0.8...v8.0.9) (2023-06-13) + + +### Bug Fixes + +* **permissions:** use correct collection name for model under module ([#624](https://github.com/ForestAdmin/forest-rails/issues/624)) ([e896763](https://github.com/ForestAdmin/forest-rails/commit/e8967631d93a8375de26f7707f8acba93f6ecc5c)) + ## [8.0.8](https://github.com/ForestAdmin/forest-rails/compare/v8.0.7...v8.0.8) (2023-05-23) diff --git a/Gemfile.lock b/Gemfile.lock index be0110da..795b8898 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - forest_liana (8.0.8) + forest_liana (8.0.9) arel-helpers bcrypt deepsort diff --git a/lib/forest_liana/version.rb b/lib/forest_liana/version.rb index 5b2f4103..644c7274 100644 --- a/lib/forest_liana/version.rb +++ b/lib/forest_liana/version.rb @@ -1,3 +1,3 @@ module ForestLiana - VERSION = "8.0.8" + VERSION = "8.0.9" end diff --git a/package.json b/package.json index 7d2352ad..9bbe1b29 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "forest-rails", - "version": "8.0.8", + "version": "8.0.9", "description": "The official Rails liana for Forest.", "directories": { "test": "test" From 3d66b3bff1ddfb3476af3b6f54bcef6f15d79247 Mon Sep 17 00:00:00 2001 From: Matthieu Date: Mon, 19 Jun 2023 15:59:36 +0200 Subject: [PATCH 19/36] fix(smartaction): register custom endpoint for load/changes hooks (#626) Co-authored-by: Nicolas Alexandre --- config/routes.rb | 5 +- config/routes/actions.rb | 12 ++ .../lib/forest_liana/collections/island.rb | 103 ++++++++++- spec/requests/actions_controller_spec.rb | 173 +++++------------- 4 files changed, 160 insertions(+), 133 deletions(-) create mode 100644 config/routes/actions.rb diff --git a/config/routes.rb b/config/routes.rb index 515d6a31..3b8c68a9 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -59,7 +59,6 @@ delete ':collection/:id', to: router delete ':collection', to: router - # Smart Actions forms value - post 'actions/:action_name/hooks/load' => 'actions#load' - post 'actions/:action_name/hooks/change' => 'actions#change' + draw(:actions) + end diff --git a/config/routes/actions.rb b/config/routes/actions.rb new file mode 100644 index 00000000..4b01af8e --- /dev/null +++ b/config/routes/actions.rb @@ -0,0 +1,12 @@ +ForestLiana.apimap.each do |collection| + if !collection.actions.empty? + collection.actions.each do |action| + if action.hooks && action.hooks[:load].present? + post action.endpoint.sub('forest', '') + '/hooks/load' => 'actions#load', action_name: ActiveSupport::Inflector.parameterize(action.name) + end + if action.hooks && action.hooks[:change].present? + post action.endpoint.sub('forest', '') + '/hooks/change' => 'actions#change', action_name: ActiveSupport::Inflector.parameterize(action.name) + end + end + end +end diff --git a/spec/dummy/lib/forest_liana/collections/island.rb b/spec/dummy/lib/forest_liana/collections/island.rb index 634cb60c..24208739 100644 --- a/spec/dummy/lib/forest_liana/collections/island.rb +++ b/spec/dummy/lib/forest_liana/collections/island.rb @@ -3,5 +3,106 @@ class Forest::Island collection :Island - action 'my_action' + foo = { + field: 'foo', + type: 'String', + default_value: nil, + enums: nil, + is_required: false, + is_read_only: false, + reference: nil, + description: nil, + widget: nil, + hook: 'on_foo_changed' + } + enum = { + field: 'enum', + type: 'Enum', + enums: %w[a b c], + } + multiple_enum = { + field: 'multipleEnum', + type: ['Enum'], + enums: %w[a b c], + } + + action 'my_action', + fields: [foo], + hooks: { + :load => -> (context) { + context[:fields] + }, + :change => { + 'on_foo_changed' => -> (context) { + foo = context[:fields].find{|field| field[:field] == 'foo'} + foo[:value] = 'baz' + context[:fields] + } + } + } + + action 'fail_action', + fields: [foo], + hooks: { + :load => -> (context) { + 1 + }, + :change => { + 'on_foo_changed' => -> (context) { + 1 + } + } + } + + action 'cheat_action', + fields: [foo], + hooks: { + :load => -> (context) { + {} + }, + :change => { + 'on_foo_changed' => -> (context) { + context[:fields]['baz'] = foo.clone.update({field: 'baz'}) + context[:fields] + } + } + } + + action 'enums_action', + endpoint: 'forest/custom/islands/enums_action', + fields: [foo, enum], + hooks: { + :change => { + 'on_foo_changed' => -> (context) { + fields = context[:fields] + enum_field = fields.find{|field| field[:field] == 'enum'} + enum_field[:enums] = %w[c d e] + fields + } + } + } + + action 'multiple_enums_action', + fields: [foo, multiple_enum], + hooks: { + :change => { + 'on_foo_changed' => -> (context) { + fields = context[:fields] + enum_field = fields.find{|field| field[:field] == 'multipleEnum'} + enum_field[:enums] = %w[c d z] + fields + } + } + } + + action 'use_user_context', + fields: [foo], + hooks: { + :load => -> (context) { + foo = context[:fields].find{|field| field[:field] == 'foo'} + foo[:value] = context[:user]['first_name'] + context[:fields] + } + } + end diff --git a/spec/requests/actions_controller_spec.rb b/spec/requests/actions_controller_spec.rb index 6c43bee0..2185f1a4 100644 --- a/spec/requests/actions_controller_spec.rb +++ b/spec/requests/actions_controller_spec.rb @@ -39,124 +39,7 @@ } describe 'hooks' do - foo = { - field: 'foo', - type: 'String', - default_value: nil, - enums: nil, - is_required: false, - is_read_only: false, - reference: nil, - description: nil, - widget: nil, - hook: 'on_foo_changed' - } - enum = { - field: 'enum', - type: 'Enum', - enums: %w[a b c], - } - multiple_enum = { - field: 'multipleEnum', - type: ['Enum'], - enums: %w[a b c], - } - - action_definition = { - name: 'my_action', - fields: [foo], - hooks: { - :load => -> (context) { - context[:fields] - }, - :change => { - 'on_foo_changed' => -> (context) { - foo = context[:fields].find{|field| field[:field] == 'foo'} - foo[:value] = 'baz' - context[:fields] - } - } - } - } - fail_action_definition = { - name: 'fail_action', - fields: [foo], - hooks: { - :load => -> (context) { - 1 - }, - :change => { - 'on_foo_changed' => -> (context) { - 1 - } - } - } - } - cheat_action_definition = { - name: 'cheat_action', - fields: [foo], - hooks: { - :load => -> (context) { - {} - }, - :change => { - 'on_foo_changed' => -> (context) { - context[:fields]['baz'] = foo.clone.update({field: 'baz'}) - context[:fields] - } - } - } - } - enums_action_definition = { - name: 'enums_action', - fields: [foo, enum], - hooks: { - :change => { - 'on_foo_changed' => -> (context) { - fields = context[:fields] - enum_field = fields.find{|field| field[:field] == 'enum'} - enum_field[:enums] = %w[c d e] - fields - } - } - } - } - - multiple_enums_action_definition = { - name: 'multiple_enums_action', - fields: [foo, multiple_enum], - hooks: { - :change => { - 'on_foo_changed' => -> (context) { - fields = context[:fields] - enum_field = fields.find{|field| field[:field] == 'multipleEnum'} - enum_field[:enums] = %w[c d z] - fields - } - } - } - } - - use_user_context_action_definition = { - name: 'use_user_context', - fields: [foo], - hooks: { - :load => -> (context) { - foo = context[:fields].find{|field| field[:field] == 'foo'} - foo[:value] = context[:user]['first_name'] - context[:fields] - } - } - } - - action = ForestLiana::Model::Action.new(action_definition) - fail_action = ForestLiana::Model::Action.new(fail_action_definition) - cheat_action = ForestLiana::Model::Action.new(cheat_action_definition) - enums_action = ForestLiana::Model::Action.new(enums_action_definition) - multiple_enums_action = ForestLiana::Model::Action.new(multiple_enums_action_definition) - use_user_context_action = ForestLiana::Model::Action.new(use_user_context_action_definition) island = ForestLiana.apimap.find {|collection| collection.name.to_s == ForestLiana.name_for(Island)} - island.actions = [action, fail_action, cheat_action, enums_action, multiple_enums_action, use_user_context_action] describe 'call /load' do params = { @@ -167,6 +50,8 @@ it 'should respond 200' do post '/forest/actions/my_action/hooks/load', params: JSON.dump(params), headers: headers + action = island.actions.select { |action| action.name == 'my_action' }.first + foo = action.fields.select { |field| field[:field] == 'foo' }.first expect(response.status).to eq(200) expect(JSON.parse(response.body)).to eq({'fields' => [foo.merge({:value => nil}).transform_keys { |key| key.to_s.camelize(:lower) }.stringify_keys]}) end @@ -190,26 +75,30 @@ it 'should return the first_name of the user who call the action' do post '/forest/actions/use_user_context/hooks/load', params: JSON.dump(params), headers: headers + action = island.actions.select { |action| action.name == 'use_user_context' }.first + foo = action.fields.select { |field| field[:field] == 'foo' }.first expect(response.status).to eq(200) expect(JSON.parse(response.body)).to eq({'fields' => [foo.merge({:value => 'Michael'}).transform_keys { |key| key.to_s.camelize(:lower) }.stringify_keys]}) end end describe 'call /change' do - updated_foo = foo.clone.merge({:previousValue => nil, :value => 'bar'}) - params = { - data: { - attributes: { - ids: [1], - fields: [updated_foo], - collection_name: 'Island', - changed_field: 'foo', - is_read_only: true + it 'should respond 200' do + action = island.actions.select { |action| action.name == 'my_action' }.first + foo = action.fields.select { |field| field[:field] == 'foo' }.first + updated_foo = foo.clone.merge({:previousValue => nil, :value => 'bar'}) + params = { + data: { + attributes: { + ids: [1], + fields: [updated_foo], + collection_name: 'Island', + changed_field: 'foo', + is_read_only: true + } } } - } - it 'should respond 200' do post '/forest/actions/my_action/hooks/change', params: JSON.dump(params), headers: headers expect(response.status).to eq(200) expected = updated_foo.clone.merge({:value => 'baz'}) @@ -226,12 +115,31 @@ end it 'should respond 500 with bad hook result type' do + action = island.actions.select { |action| action.name == 'fail_action' }.first + foo = action.fields.select { |field| field[:field] == 'foo' }.first + updated_foo = foo.clone.merge({:previousValue => nil, :value => 'bar'}) + params = { + data: { + attributes: { + ids: [1], + fields: [updated_foo], + collection_name: 'Island', + changed_field: 'foo', + is_read_only: true + } + } + } + post '/forest/actions/fail_action/hooks/change', params: JSON.dump(params), headers: headers expect(response.status).to eq(500) expect(JSON.parse(response.body)).to eq({'error' => 'Error in smart action load hook: hook must return an array of fields'}) end it 'should reset value when enums has changed' do + action = island.actions.select { |action| action.name == 'enums_action' }.first + foo = action.fields.select { |field| field[:field] == 'foo' }.first + enum = action.fields.select { |field| field[:field] == 'enum' }.first + updated_foo = foo.clone.merge({:previousValue => nil, :value => 'bar'}) updated_enum = enum.clone.merge({:previousValue => nil, :value => 'a'}) # set value to a p = { data: { @@ -243,7 +151,8 @@ } } } - post '/forest/actions/enums_action/hooks/change', params: JSON.dump(p), headers: headers + + post '/forest/custom/islands/enums_action/hooks/change', params: JSON.dump(p), headers: headers expect(response.status).to eq(200) expected_enum = updated_enum.clone.merge({ :enums => %w[c d e], :value => nil, :widgetEdit => nil}) @@ -258,6 +167,9 @@ end it 'should not reset value when every enum values are in the enums definition' do + action = island.actions.select { |action| action.name == 'multiple_enums_action' }.first + foo = action.fields.select { |field| field[:field] == 'foo' }.first + multiple_enum = action.fields.select { |field| field[:field] == 'multipleEnum' }.first updated_multiple_enum = multiple_enum.clone.merge({:previousValue => nil, :value => %w[c]}) p = { data: { @@ -284,6 +196,9 @@ end it 'should reset value when one of the enum values is not in the enums definition' do + action = island.actions.select { |action| action.name == 'multiple_enums_action' }.first + foo = action.fields.select { |field| field[:field] == 'foo' }.first + multiple_enum = action.fields.select { |field| field[:field] == 'multipleEnum' }.first wrongly_updated_multiple_enum = multiple_enum.clone.merge({:previousValue => nil, :value => %w[a b]}) p = { data: { From f90b1aa598ecf61a66407f3a57dac1d7f59f2c63 Mon Sep 17 00:00:00 2001 From: Forest Bot Date: Mon, 19 Jun 2023 14:01:33 +0000 Subject: [PATCH 20/36] chore(release): 8.0.10 [skip ci] ## [8.0.10](https://github.com/ForestAdmin/forest-rails/compare/v8.0.9...v8.0.10) (2023-06-19) ### Bug Fixes * **smartaction:** register custom endpoint for load/changes hooks ([#626](https://github.com/ForestAdmin/forest-rails/issues/626)) ([3d66b3b](https://github.com/ForestAdmin/forest-rails/commit/3d66b3bff1ddfb3476af3b6f54bcef6f15d79247)) --- CHANGELOG.md | 7 +++++++ Gemfile.lock | 2 +- lib/forest_liana/version.rb | 2 +- package.json | 2 +- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cbc1b5b8..74593c61 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [8.0.10](https://github.com/ForestAdmin/forest-rails/compare/v8.0.9...v8.0.10) (2023-06-19) + + +### Bug Fixes + +* **smartaction:** register custom endpoint for load/changes hooks ([#626](https://github.com/ForestAdmin/forest-rails/issues/626)) ([3d66b3b](https://github.com/ForestAdmin/forest-rails/commit/3d66b3bff1ddfb3476af3b6f54bcef6f15d79247)) + ## [8.0.9](https://github.com/ForestAdmin/forest-rails/compare/v8.0.8...v8.0.9) (2023-06-13) diff --git a/Gemfile.lock b/Gemfile.lock index 795b8898..d0c8e63d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - forest_liana (8.0.9) + forest_liana (8.0.10) arel-helpers bcrypt deepsort diff --git a/lib/forest_liana/version.rb b/lib/forest_liana/version.rb index 644c7274..64677b83 100644 --- a/lib/forest_liana/version.rb +++ b/lib/forest_liana/version.rb @@ -1,3 +1,3 @@ module ForestLiana - VERSION = "8.0.9" + VERSION = "8.0.10" end diff --git a/package.json b/package.json index 9bbe1b29..4fc540c5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "forest-rails", - "version": "8.0.9", + "version": "8.0.10", "description": "The official Rails liana for Forest.", "directories": { "test": "test" From edc45aa0d09f06d89acec3ac835347129aa35d7c Mon Sep 17 00:00:00 2001 From: Matthieu Date: Thu, 29 Jun 2023 16:55:37 +0200 Subject: [PATCH 21/36] fix: reporter error on dissociate action (#627) --- app/controllers/forest_liana/associations_controller.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/controllers/forest_liana/associations_controller.rb b/app/controllers/forest_liana/associations_controller.rb index 2f171e87..f0e5785c 100644 --- a/app/controllers/forest_liana/associations_controller.rb +++ b/app/controllers/forest_liana/associations_controller.rb @@ -77,7 +77,8 @@ def dissociate head :no_content rescue => error - FOREST_LOGGER.error "Association Associate error: #{error}\n#{format_stacktrace(error)}" + FOREST_REPORTER.report error + FOREST_LOGGER.error "Association Dissociate error: #{error}\n#{format_stacktrace(error)}" internal_server_error end end From 4672bb1b2c556402765dc3c5e7f8777416c697c9 Mon Sep 17 00:00:00 2001 From: Forest Bot Date: Thu, 29 Jun 2023 14:58:14 +0000 Subject: [PATCH 22/36] chore(release): 8.0.11 [skip ci] ## [8.0.11](https://github.com/ForestAdmin/forest-rails/compare/v8.0.10...v8.0.11) (2023-06-29) ### Bug Fixes * reporter error on dissociate action ([#627](https://github.com/ForestAdmin/forest-rails/issues/627)) ([edc45aa](https://github.com/ForestAdmin/forest-rails/commit/edc45aa0d09f06d89acec3ac835347129aa35d7c)) --- CHANGELOG.md | 7 +++++++ Gemfile.lock | 2 +- lib/forest_liana/version.rb | 2 +- package.json | 2 +- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 74593c61..7d765bfb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [8.0.11](https://github.com/ForestAdmin/forest-rails/compare/v8.0.10...v8.0.11) (2023-06-29) + + +### Bug Fixes + +* reporter error on dissociate action ([#627](https://github.com/ForestAdmin/forest-rails/issues/627)) ([edc45aa](https://github.com/ForestAdmin/forest-rails/commit/edc45aa0d09f06d89acec3ac835347129aa35d7c)) + ## [8.0.10](https://github.com/ForestAdmin/forest-rails/compare/v8.0.9...v8.0.10) (2023-06-19) diff --git a/Gemfile.lock b/Gemfile.lock index d0c8e63d..f21f0f96 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - forest_liana (8.0.10) + forest_liana (8.0.11) arel-helpers bcrypt deepsort diff --git a/lib/forest_liana/version.rb b/lib/forest_liana/version.rb index 64677b83..059fc736 100644 --- a/lib/forest_liana/version.rb +++ b/lib/forest_liana/version.rb @@ -1,3 +1,3 @@ module ForestLiana - VERSION = "8.0.10" + VERSION = "8.0.11" end diff --git a/package.json b/package.json index 4fc540c5..daa65b0c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "forest-rails", - "version": "8.0.10", + "version": "8.0.11", "description": "The official Rails liana for Forest.", "directories": { "test": "test" From ff2e1b5231393a8adfbef3b9c4abb135999e73ad Mon Sep 17 00:00:00 2001 From: Matthieu Date: Fri, 7 Jul 2023 11:36:23 +0200 Subject: [PATCH 23/36] fix: allow charts with dynamic query using record id (#628) --- app/services/forest_liana/ability/permission.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/services/forest_liana/ability/permission.rb b/app/services/forest_liana/ability/permission.rb index d13a46bd..7d1eec39 100644 --- a/app/services/forest_liana/ability/permission.rb +++ b/app/services/forest_liana/ability/permission.rb @@ -51,7 +51,7 @@ def is_chart_authorized?(user, parameters) parameters.delete('action') parameters.delete('collection') parameters.delete('contextVariables') - + parameters.delete('record_id') hash_request = "#{parameters['type']}:#{Digest::SHA1.hexdigest(parameters.deep_sort.to_s)}" allowed = get_chart_data(user['rendering_id']).to_s.include? hash_request From eec1873b6750410795c6a8d3cd490d3a5007f722 Mon Sep 17 00:00:00 2001 From: Forest Bot Date: Fri, 7 Jul 2023 09:38:40 +0000 Subject: [PATCH 24/36] chore(release): 8.0.12 [skip ci] ## [8.0.12](https://github.com/ForestAdmin/forest-rails/compare/v8.0.11...v8.0.12) (2023-07-07) ### Bug Fixes * allow charts with dynamic query using record id ([#628](https://github.com/ForestAdmin/forest-rails/issues/628)) ([ff2e1b5](https://github.com/ForestAdmin/forest-rails/commit/ff2e1b5231393a8adfbef3b9c4abb135999e73ad)) --- CHANGELOG.md | 7 +++++++ Gemfile.lock | 2 +- lib/forest_liana/version.rb | 2 +- package.json | 2 +- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d765bfb..3893cd2e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [8.0.12](https://github.com/ForestAdmin/forest-rails/compare/v8.0.11...v8.0.12) (2023-07-07) + + +### Bug Fixes + +* allow charts with dynamic query using record id ([#628](https://github.com/ForestAdmin/forest-rails/issues/628)) ([ff2e1b5](https://github.com/ForestAdmin/forest-rails/commit/ff2e1b5231393a8adfbef3b9c4abb135999e73ad)) + ## [8.0.11](https://github.com/ForestAdmin/forest-rails/compare/v8.0.10...v8.0.11) (2023-06-29) diff --git a/Gemfile.lock b/Gemfile.lock index f21f0f96..81127cec 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - forest_liana (8.0.11) + forest_liana (8.0.12) arel-helpers bcrypt deepsort diff --git a/lib/forest_liana/version.rb b/lib/forest_liana/version.rb index 059fc736..d0dac885 100644 --- a/lib/forest_liana/version.rb +++ b/lib/forest_liana/version.rb @@ -1,3 +1,3 @@ module ForestLiana - VERSION = "8.0.11" + VERSION = "8.0.12" end diff --git a/package.json b/package.json index daa65b0c..f618c039 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "forest-rails", - "version": "8.0.11", + "version": "8.0.12", "description": "The official Rails liana for Forest.", "directories": { "test": "test" From da1f8b6cf22d1701a83c40e2a79d056622566715 Mon Sep 17 00:00:00 2001 From: Matthieu Date: Thu, 5 Oct 2023 14:06:43 +0200 Subject: [PATCH 25/36] fix: destroy record with restriction on children (#632) close #630 --- .../forest_liana/resources_controller.rb | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/app/controllers/forest_liana/resources_controller.rb b/app/controllers/forest_liana/resources_controller.rb index b1dfb931..67ca1013 100644 --- a/app/controllers/forest_liana/resources_controller.rb +++ b/app/controllers/forest_liana/resources_controller.rb @@ -129,7 +129,7 @@ def update def destroy forest_authorize!('delete', forest_user, @resource) - begin + begin collection_name = ForestLiana.name_for(@resource) scoped_records = ForestLiana::ScopeManager.apply_scopes_on_records(@resource, forest_user, collection_name, params[:timezone]) @@ -137,9 +137,12 @@ def destroy return render serializer: nil, json: { status: 404 }, status: :not_found end - scoped_records.destroy(params[:id]) - - head :no_content + if scoped_records.destroy(params[:id]) + head :no_content + else + restrict_error = ActiveRecord::DeleteRestrictionError.new + render json: { errors: [{ status: :bad_request, detail: restrict_error.message }] }, status: :bad_request + end rescue => error FOREST_REPORTER.report error FOREST_LOGGER.error "Record Destroy error: #{error}\n#{format_stacktrace(error)}" @@ -151,9 +154,14 @@ def destroy_bulk forest_authorize!('delete', forest_user, @resource) begin ids = ForestLiana::ResourcesGetter.get_ids_from_request(params, forest_user) - @resource.destroy(ids) if ids&.any? - - head :no_content + @resource.transaction do + ids.each do |id| + record = @resource.find(id) + record.destroy! + end + end + rescue ActiveRecord::RecordNotDestroyed => error + render json: { errors: [{ status: :bad_request, detail: error.message }] }, status: :bad_request rescue => error FOREST_REPORTER.report error FOREST_LOGGER.error "Records Destroy error: #{error}\n#{format_stacktrace(error)}" From 9a80620f85833f6816ee1fd5f1a05ec2537dfada Mon Sep 17 00:00:00 2001 From: Forest Bot Date: Thu, 5 Oct 2023 12:09:02 +0000 Subject: [PATCH 26/36] chore(release): 8.0.13 [skip ci] ## [8.0.13](https://github.com/ForestAdmin/forest-rails/compare/v8.0.12...v8.0.13) (2023-10-05) ### Bug Fixes * destroy record with restriction on children ([#632](https://github.com/ForestAdmin/forest-rails/issues/632)) ([da1f8b6](https://github.com/ForestAdmin/forest-rails/commit/da1f8b6cf22d1701a83c40e2a79d056622566715)), closes [#630](https://github.com/ForestAdmin/forest-rails/issues/630) --- CHANGELOG.md | 7 +++++++ Gemfile.lock | 2 +- lib/forest_liana/version.rb | 2 +- package.json | 2 +- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3893cd2e..2be1f18a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [8.0.13](https://github.com/ForestAdmin/forest-rails/compare/v8.0.12...v8.0.13) (2023-10-05) + + +### Bug Fixes + +* destroy record with restriction on children ([#632](https://github.com/ForestAdmin/forest-rails/issues/632)) ([da1f8b6](https://github.com/ForestAdmin/forest-rails/commit/da1f8b6cf22d1701a83c40e2a79d056622566715)), closes [#630](https://github.com/ForestAdmin/forest-rails/issues/630) + ## [8.0.12](https://github.com/ForestAdmin/forest-rails/compare/v8.0.11...v8.0.12) (2023-07-07) diff --git a/Gemfile.lock b/Gemfile.lock index 81127cec..0633824e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - forest_liana (8.0.12) + forest_liana (8.0.13) arel-helpers bcrypt deepsort diff --git a/lib/forest_liana/version.rb b/lib/forest_liana/version.rb index d0dac885..cd495bb8 100644 --- a/lib/forest_liana/version.rb +++ b/lib/forest_liana/version.rb @@ -1,3 +1,3 @@ module ForestLiana - VERSION = "8.0.12" + VERSION = "8.0.13" end diff --git a/package.json b/package.json index f618c039..8feb2f27 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "forest-rails", - "version": "8.0.12", + "version": "8.0.13", "description": "The official Rails liana for Forest.", "directories": { "test": "test" From e356b838ebd8b426d189f076414841216b5198ab Mon Sep 17 00:00:00 2001 From: Matthieu Date: Mon, 16 Oct 2023 11:34:11 +0200 Subject: [PATCH 27/36] fix(logger): format of the datetime (#634) close #633 --- config/initializers/logger.rb | 2 +- spec/config/initializers/logger_spec.rb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/config/initializers/logger.rb b/config/initializers/logger.rb index d05a9646..7b0ccf8e 100644 --- a/config/initializers/logger.rb +++ b/config/initializers/logger.rb @@ -15,7 +15,7 @@ def log } logger.formatter = proc do |severity, datetime, progname, message| - displayed_message = "[#{datetime.to_s(:db)}] Forest 🌳🌳🌳 " \ + displayed_message = "[#{datetime.to_s}] Forest 🌳🌳🌳 " \ "#{message}\n" "\e[#{logger_colors[severity.to_sym]}m#{displayed_message}\033[0m" end diff --git a/spec/config/initializers/logger_spec.rb b/spec/config/initializers/logger_spec.rb index 438083b2..a703c212 100644 --- a/spec/config/initializers/logger_spec.rb +++ b/spec/config/initializers/logger_spec.rb @@ -21,8 +21,8 @@ module ForestLiana expect(Logger.log.is_a?(::Logger)).to be_truthy # RegExp is used to check for the forestadmin logger format - expect { Logger.log.error "[error] default logger" }.to output(/\[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\] Forest .* \[error\]/).to_stdout_from_any_process - expect { Logger.log.info "[info] default logger" }.to output(/\[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\] Forest .* \[info\]/).to_stdout_from_any_process + expect { Logger.log.error "[error] default logger" }.to output(/\[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} (\+|\-)(\d{4})\] Forest .* \[error\]/).to_stdout_from_any_process + expect { Logger.log.info "[info] default logger" }.to output(/\[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} (\+|\-)(\d{4})\] Forest .* \[info\]/).to_stdout_from_any_process end end end From a19ca376eb6330be37ee566de324999cdbd542b2 Mon Sep 17 00:00:00 2001 From: Forest Bot Date: Mon, 16 Oct 2023 09:39:29 +0000 Subject: [PATCH 28/36] chore(release): 8.0.14 [skip ci] ## [8.0.14](https://github.com/ForestAdmin/forest-rails/compare/v8.0.13...v8.0.14) (2023-10-16) ### Bug Fixes * **logger:** format of the datetime ([#634](https://github.com/ForestAdmin/forest-rails/issues/634)) ([e356b83](https://github.com/ForestAdmin/forest-rails/commit/e356b838ebd8b426d189f076414841216b5198ab)), closes [#633](https://github.com/ForestAdmin/forest-rails/issues/633) --- CHANGELOG.md | 7 +++++++ Gemfile.lock | 2 +- lib/forest_liana/version.rb | 2 +- package.json | 2 +- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2be1f18a..536eb138 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [8.0.14](https://github.com/ForestAdmin/forest-rails/compare/v8.0.13...v8.0.14) (2023-10-16) + + +### Bug Fixes + +* **logger:** format of the datetime ([#634](https://github.com/ForestAdmin/forest-rails/issues/634)) ([e356b83](https://github.com/ForestAdmin/forest-rails/commit/e356b838ebd8b426d189f076414841216b5198ab)), closes [#633](https://github.com/ForestAdmin/forest-rails/issues/633) + ## [8.0.13](https://github.com/ForestAdmin/forest-rails/compare/v8.0.12...v8.0.13) (2023-10-05) diff --git a/Gemfile.lock b/Gemfile.lock index 0633824e..d48bebb1 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - forest_liana (8.0.13) + forest_liana (8.0.14) arel-helpers bcrypt deepsort diff --git a/lib/forest_liana/version.rb b/lib/forest_liana/version.rb index cd495bb8..eac9d2d5 100644 --- a/lib/forest_liana/version.rb +++ b/lib/forest_liana/version.rb @@ -1,3 +1,3 @@ module ForestLiana - VERSION = "8.0.13" + VERSION = "8.0.14" end diff --git a/package.json b/package.json index 8feb2f27..894d5987 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "forest-rails", - "version": "8.0.13", + "version": "8.0.14", "description": "The official Rails liana for Forest.", "directories": { "test": "test" From 9a7590b5084ed4588fab09bd903822d05a0930fc Mon Sep 17 00:00:00 2001 From: Matthieu Date: Mon, 23 Oct 2023 16:46:47 +0200 Subject: [PATCH 29/36] fix(permissions): fetch permissions return an exception when the server doesn't return an 200 response (#635) --- .../forest_liana/application_controller.rb | 1 + app/services/forest_liana/ability/fetch.rb | 18 +++++------------- .../forest_liana/ability/permission.rb | 2 +- .../forest_liana/ability/permission_spec.rb | 11 +++++++++++ 4 files changed, 18 insertions(+), 14 deletions(-) diff --git a/app/controllers/forest_liana/application_controller.rb b/app/controllers/forest_liana/application_controller.rb index f53b4797..804fc67c 100644 --- a/app/controllers/forest_liana/application_controller.rb +++ b/app/controllers/forest_liana/application_controller.rb @@ -4,6 +4,7 @@ module ForestLiana class ApplicationController < ForestLiana::BaseController rescue_from ForestLiana::Ability::Exceptions::AccessDenied, with: :render_error + rescue_from ForestLiana::Errors::HTTP403Error, with: :render_error rescue_from ForestLiana::Errors::HTTP422Error, with: :render_error def self.papertrail? diff --git a/app/services/forest_liana/ability/fetch.rb b/app/services/forest_liana/ability/fetch.rb index 1766ee86..6f0be64b 100644 --- a/app/services/forest_liana/ability/fetch.rb +++ b/app/services/forest_liana/ability/fetch.rb @@ -2,20 +2,12 @@ module ForestLiana module Ability module Fetch def get_permissions(route) - begin - response = ForestLiana::ForestApiRequester.get(route) + response = ForestLiana::ForestApiRequester.get(route) - if response.is_a?(Net::HTTPOK) - JSON.parse(response.body) - else - raise "Forest API returned an #{ForestLiana::Errors::HTTPErrorHelper.format(response)}" - end - rescue => exception - FOREST_REPORTER.report exception - FOREST_LOGGER.error 'Cannot retrieve the permissions from the Forest server.' - FOREST_LOGGER.error 'Which was caused by:' - ForestLiana::Errors::ExceptionHelper.recursively_print(exception, margin: ' ', is_error: true) - nil + if response.is_a?(Net::HTTPOK) + JSON.parse(response.body) + else + raise ForestLiana::Errors::HTTP403Error.new("Permission could not be retrieved") end end end diff --git a/app/services/forest_liana/ability/permission.rb b/app/services/forest_liana/ability/permission.rb index 7d1eec39..baa018c8 100644 --- a/app/services/forest_liana/ability/permission.rb +++ b/app/services/forest_liana/ability/permission.rb @@ -6,7 +6,7 @@ module Ability module Permission include Fetch - TTL = (ENV['FOREST_PERMISSIONS_EXPIRATION_IN_SECONDS'] || 1).to_i.second + TTL = (ENV['FOREST_PERMISSIONS_EXPIRATION_IN_SECONDS'] || 900).to_i.second def is_crud_authorized?(action, user, collection) return true unless has_permission_system? diff --git a/spec/services/forest_liana/ability/permission_spec.rb b/spec/services/forest_liana/ability/permission_spec.rb index 34b8d74b..bc375528 100644 --- a/spec/services/forest_liana/ability/permission_spec.rb +++ b/spec/services/forest_liana/ability/permission_spec.rb @@ -327,6 +327,17 @@ module Ability expect {dummy_class.is_smart_action_authorized?(user, String, parameters, '/forest/actions/my_action', 'POST')}.to raise_error(ForestLiana::Errors::ExpectedError, 'The collection String doesn\'t exist') end end + + describe 'when the server doesn\'t return an success response' do + before do + Rails.cache.clear + end + + it 'should return an exception' do + allow(ForestLiana::ForestApiRequester).to receive(:get).and_return(instance_double(HTTParty::Response, code: 500, body: nil)) + expect { dummy_class.is_crud_authorized?('browse', user, Island.first) }.to raise_error(ForestLiana::Errors::HTTP403Error, 'Permission could not be retrieved') + end + end end end end From 92657b8a2c2627596e09333c1b90eb003085c3c3 Mon Sep 17 00:00:00 2001 From: Forest Bot Date: Mon, 23 Oct 2023 14:49:25 +0000 Subject: [PATCH 30/36] chore(release): 8.0.15 [skip ci] ## [8.0.15](https://github.com/ForestAdmin/forest-rails/compare/v8.0.14...v8.0.15) (2023-10-23) ### Bug Fixes * **permissions:** fetch permissions return an exception when the server doesn't return an 200 response ([#635](https://github.com/ForestAdmin/forest-rails/issues/635)) ([9a7590b](https://github.com/ForestAdmin/forest-rails/commit/9a7590b5084ed4588fab09bd903822d05a0930fc)) --- CHANGELOG.md | 7 +++++++ Gemfile.lock | 2 +- lib/forest_liana/version.rb | 2 +- package.json | 2 +- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 536eb138..7dae9813 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [8.0.15](https://github.com/ForestAdmin/forest-rails/compare/v8.0.14...v8.0.15) (2023-10-23) + + +### Bug Fixes + +* **permissions:** fetch permissions return an exception when the server doesn't return an 200 response ([#635](https://github.com/ForestAdmin/forest-rails/issues/635)) ([9a7590b](https://github.com/ForestAdmin/forest-rails/commit/9a7590b5084ed4588fab09bd903822d05a0930fc)) + ## [8.0.14](https://github.com/ForestAdmin/forest-rails/compare/v8.0.13...v8.0.14) (2023-10-16) diff --git a/Gemfile.lock b/Gemfile.lock index d48bebb1..cda029bd 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - forest_liana (8.0.14) + forest_liana (8.0.15) arel-helpers bcrypt deepsort diff --git a/lib/forest_liana/version.rb b/lib/forest_liana/version.rb index eac9d2d5..8e469a6c 100644 --- a/lib/forest_liana/version.rb +++ b/lib/forest_liana/version.rb @@ -1,3 +1,3 @@ module ForestLiana - VERSION = "8.0.14" + VERSION = "8.0.15" end diff --git a/package.json b/package.json index 894d5987..3b90abdb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "forest-rails", - "version": "8.0.14", + "version": "8.0.15", "description": "The official Rails liana for Forest.", "directories": { "test": "test" From 1a69e2f0ce308b060454ca98ab0d08fd8f14a862 Mon Sep 17 00:00:00 2001 From: Matthieu Date: Thu, 16 Nov 2023 11:13:48 +0100 Subject: [PATCH 31/36] fix(authentication): return errors detail instead of generic error 500 (#636) --- .../forest_liana/authentication_controller.rb | 17 ++++ app/services/forest_liana/authentication.rb | 2 - config/initializers/errors.rb | 11 +++ spec/requests/authentications_spec.rb | 88 ++++++++++++------- 4 files changed, 83 insertions(+), 35 deletions(-) diff --git a/app/controllers/forest_liana/authentication_controller.rb b/app/controllers/forest_liana/authentication_controller.rb index ce6b37e4..e7be06b7 100644 --- a/app/controllers/forest_liana/authentication_controller.rb +++ b/app/controllers/forest_liana/authentication_controller.rb @@ -39,6 +39,8 @@ def start_authentication end def authentication_callback + return authentication_exception if params.key?(:error) + begin token = @authentication_service.verify_code_and_generate_token(params) @@ -55,6 +57,21 @@ def authentication_callback end end + def authentication_exception + begin + raise ForestLiana::Errors::AuthenticationOpenIdClientException.new(params[:error], params[:error_description], params[:state]) + rescue => error + FOREST_REPORTER.report error + FOREST_LOGGER.error "AuthenticationOpenIdClientException: #{error.error_description}" + + render json: { + error: error.error, + error_description: error.error_description, + state: error.state + }, status: :unauthorized + end + end + def logout begin if cookies.has_key?(:forest_session_token) diff --git a/app/services/forest_liana/authentication.rb b/app/services/forest_liana/authentication.rb index 2b6d4ad4..9591b953 100644 --- a/app/services/forest_liana/authentication.rb +++ b/app/services/forest_liana/authentication.rb @@ -38,8 +38,6 @@ def parse_state(state) raise ForestLiana::MESSAGES[:SERVER_TRANSACTION][:INVALID_STATE_MISSING] end - rendering_id = nil - begin parsed_state = JSON.parse(state.gsub("'",'"').gsub('=>',':')) rendering_id = parsed_state["renderingId"].to_s diff --git a/config/initializers/errors.rb b/config/initializers/errors.rb index c6550d7b..0b88896d 100644 --- a/config/initializers/errors.rb +++ b/config/initializers/errors.rb @@ -30,6 +30,17 @@ def initialize(action_name=nil, field_name=nil, hook_name=nil) end end + class AuthenticationOpenIdClientException < StandardError + attr_reader :error, :error_description, :state + + def initialize(error, error_description, state) + super(error_description) + @error = error + @error_description = error_description + @state = state + end + end + class ExpectedError < StandardError attr_reader :error_code, :status, :message, :name diff --git a/spec/requests/authentications_spec.rb b/spec/requests/authentications_spec.rb index f15aadfa..9713ba34 100644 --- a/spec/requests/authentications_spec.rb +++ b/spec/requests/authentications_spec.rb @@ -44,44 +44,66 @@ end describe "GET /authentication/callback" do - before() do - response = '{"data":{"id":666,"attributes":{"first_name":"Alice","last_name":"Doe","email":"alice@forestadmin.com","teams":[1,2,3],"role":"Test","tags":[{"key":"city","value":"Paris"}]}}}' - allow(ForestLiana::ForestApiRequester).to receive(:get).with( - "/liana/v2/renderings/42/authorization", { :headers => { "forest-token" => "THE-ACCESS-TOKEN" }, :query => {} } - ).and_return( - instance_double(HTTParty::Response, :body => response, :code => 200) - ) - - get ForestLiana::Engine.routes.url_helpers.authentication_callback_path + "?code=THE-CODE&state=#{CGI::escape('{"renderingId":42}')}" - end + context 'when the response is a 200' do + before() do + response = '{"data":{"id":666,"attributes":{"first_name":"Alice","last_name":"Doe","email":"alice@forestadmin.com","teams":[1,2,3],"role":"Test","tags":[{"key":"city","value":"Paris"}]}}}' + allow(ForestLiana::ForestApiRequester).to receive(:get).with( + "/liana/v2/renderings/42/authorization", { :headers => { "forest-token" => "THE-ACCESS-TOKEN" }, :query => {} } + ).and_return( + instance_double(HTTParty::Response, :body => response, :code => 200) + ) - it "should respond with a 200 code" do - expect(response).to have_http_status(200) - end + get ForestLiana::Engine.routes.url_helpers.authentication_callback_path + "?code=THE-CODE&state=#{CGI::escape('{"renderingId":42}')}" + end - it "should return a valid authentication token" do - body = JSON.parse(response.body, :symbolize_names => true); + it "should respond with a 200 code" do + expect(response).to have_http_status(200) + end - token = body[:token] - decoded = JWT.decode(token, ForestLiana.auth_secret, true, { algorithm: 'HS256' })[0] + it "should return a valid authentication token" do + body = JSON.parse(response.body, :symbolize_names => true); - expected_token_data = { - "id" => 666, - "email" => 'alice@forestadmin.com', - "rendering_id" => "42", - "first_name" => 'Alice', - "last_name" => 'Doe', - "team" => 1, - "role" => "Test", - } + token = body[:token] + decoded = JWT.decode(token, ForestLiana.auth_secret, true, { algorithm: 'HS256' })[0] - expect(decoded).to include(expected_token_data) - tags = decoded['tags'] - expect(tags.length).to eq(1) - expect(tags[0]['key']).to eq("city") - expect(tags[0]['value']).to eq("Paris") - expect(body).to eq({ token: token, tokenData: decoded.deep_symbolize_keys! }) - expect(response).to have_http_status(200) + expected_token_data = { + "id" => 666, + "email" => 'alice@forestadmin.com', + "rendering_id" => "42", + "first_name" => 'Alice', + "last_name" => 'Doe', + "team" => 1, + "role" => "Test", + } + + expect(decoded).to include(expected_token_data) + tags = decoded['tags'] + expect(tags.length).to eq(1) + expect(tags[0]['key']).to eq("city") + expect(tags[0]['value']).to eq("Paris") + expect(body).to eq({ token: token, tokenData: decoded.deep_symbolize_keys! }) + expect(response).to have_http_status(200) + end + end + + context 'when the response is not a 200' do + before() do + get ForestLiana::Engine.routes.url_helpers.authentication_callback_path, + params: { + error: 'TrialBlockedError', + error_description: 'Your free trial has ended. We hope you enjoyed your experience with Forest Admin.', + state: '{"renderingId":100}' + }, + headers: { + 'Accept' => 'application/json', + 'Content-Type' => 'application/json', + } + end + + it "should respond with a 401 code" do + expect(response).to have_http_status(401) + expect(response.body).to eq('{"error":"TrialBlockedError","error_description":"Your free trial has ended. We hope you enjoyed your experience with Forest Admin.","state":"{\"renderingId\":100}"}') + end end end From 0633364503533efe52d4985ea5f6aedb934b5e11 Mon Sep 17 00:00:00 2001 From: Forest Bot Date: Thu, 16 Nov 2023 10:15:45 +0000 Subject: [PATCH 32/36] chore(release): 8.0.16 [skip ci] ## [8.0.16](https://github.com/ForestAdmin/forest-rails/compare/v8.0.15...v8.0.16) (2023-11-16) ### Bug Fixes * **authentication:** return errors detail instead of generic error 500 ([#636](https://github.com/ForestAdmin/forest-rails/issues/636)) ([1a69e2f](https://github.com/ForestAdmin/forest-rails/commit/1a69e2f0ce308b060454ca98ab0d08fd8f14a862)) --- CHANGELOG.md | 7 +++++++ Gemfile.lock | 2 +- lib/forest_liana/version.rb | 2 +- package.json | 2 +- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7dae9813..96b132d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [8.0.16](https://github.com/ForestAdmin/forest-rails/compare/v8.0.15...v8.0.16) (2023-11-16) + + +### Bug Fixes + +* **authentication:** return errors detail instead of generic error 500 ([#636](https://github.com/ForestAdmin/forest-rails/issues/636)) ([1a69e2f](https://github.com/ForestAdmin/forest-rails/commit/1a69e2f0ce308b060454ca98ab0d08fd8f14a862)) + ## [8.0.15](https://github.com/ForestAdmin/forest-rails/compare/v8.0.14...v8.0.15) (2023-10-23) diff --git a/Gemfile.lock b/Gemfile.lock index cda029bd..770036e6 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - forest_liana (8.0.15) + forest_liana (8.0.16) arel-helpers bcrypt deepsort diff --git a/lib/forest_liana/version.rb b/lib/forest_liana/version.rb index 8e469a6c..cbc628aa 100644 --- a/lib/forest_liana/version.rb +++ b/lib/forest_liana/version.rb @@ -1,3 +1,3 @@ module ForestLiana - VERSION = "8.0.15" + VERSION = "8.0.16" end diff --git a/package.json b/package.json index 3b90abdb..a2defba2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "forest-rails", - "version": "8.0.15", + "version": "8.0.16", "description": "The official Rails liana for Forest.", "directories": { "test": "test" From 02679b584b84eda3d1f0be6e6482d130b53b4d19 Mon Sep 17 00:00:00 2001 From: NicolasAlexandre Date: Tue, 5 Dec 2023 10:27:41 +0100 Subject: [PATCH 33/36] fix: security vulnerabilities RCE on 8.x.x (#638) --- .github/workflows/build.yml | 3 +- .releaserc.js | 2 +- app/services/forest_liana/stat_getter.rb | 8 + .../forest_liana/value_stat_getter.rb | 13 +- .../forest_liana/line_stat_getter_spec.rb | 13 ++ .../forest_liana/pie_stat_getter_spec.rb | 157 ++++++++++-------- .../forest_liana/value_stat_getter_spec.rb | 133 +++++++++------ 7 files changed, 199 insertions(+), 130 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7683845a..abdaa6ac 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -5,6 +5,7 @@ on: branches: - main - beta + - 7.x.x pull_request: env: @@ -72,7 +73,7 @@ jobs: name: Release runs-on: ubuntu-latest needs: [lint, test] - if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/beta') + if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/beta' || github.ref == 'refs/heads/7.x.x') steps: - uses: actions/checkout@v2 with: diff --git a/.releaserc.js b/.releaserc.js index c41d12a9..f7fae122 100644 --- a/.releaserc.js +++ b/.releaserc.js @@ -1,5 +1,5 @@ module.exports = { - branches: ['main', {name: 'beta', prerelease: true}], + branches: ['main', '+([0-9])?(.{+([0-9]),x}).x', {name: 'beta', prerelease: true}], plugins: [ [ '@semantic-release/commit-analyzer', { diff --git a/app/services/forest_liana/stat_getter.rb b/app/services/forest_liana/stat_getter.rb index ce96ca9d..b8e10343 100644 --- a/app/services/forest_liana/stat_getter.rb +++ b/app/services/forest_liana/stat_getter.rb @@ -6,9 +6,17 @@ def initialize(resource, params, forest_user) @resource = resource @params = params @user = forest_user + + validate_params compute_includes end + def validate_params + if @params.key?(:aggregator) && !%w[count sum avg max min].include?(@params[:aggregator].downcase) + raise ForestLiana::Errors::HTTP422Error.new('Invalid aggregate function') + end + end + def get_resource super @resource.reorder('') diff --git a/app/services/forest_liana/value_stat_getter.rb b/app/services/forest_liana/value_stat_getter.rb index 8d0f7255..1dc6a1c7 100644 --- a/app/services/forest_liana/value_stat_getter.rb +++ b/app/services/forest_liana/value_stat_getter.rb @@ -19,24 +19,25 @@ def perform end @record = Model::Stat.new(value: { - countCurrent: count(resource), - countPrevious: previous_value ? count(previous_value) : nil + countCurrent: aggregate(resource), + countPrevious: previous_value ? aggregate(previous_value) : nil }) end private - def count(value) - uniq = @params[:aggregator].downcase == 'count' + def aggregate(value) + aggregator = @params[:aggregator].downcase + uniq = aggregator == 'count' if Rails::VERSION::MAJOR >= 4 if uniq # NOTICE: uniq is deprecated since Rails 5.0 value = Rails::VERSION::MAJOR >= 5 ? value.distinct : value.uniq end - value.send(@params[:aggregator].downcase, aggregate_field) + value.send(aggregator, aggregate_field) else - value.send(@params[:aggregator].downcase, aggregate_field, distinct: uniq) + value.send(aggregator, aggregate_field, distinct: uniq) end end diff --git a/spec/services/forest_liana/line_stat_getter_spec.rb b/spec/services/forest_liana/line_stat_getter_spec.rb index 754d0248..c766d521 100644 --- a/spec/services/forest_liana/line_stat_getter_spec.rb +++ b/spec/services/forest_liana/line_stat_getter_spec.rb @@ -9,6 +9,19 @@ module ForestLiana allow(ForestLiana::ScopeManager).to receive(:fetch_scopes).and_return(scopes) end + describe 'with not allowed aggregator' do + it 'should raise an error' do + expect { + LineStatGetter.new(Owner, { + timezone: "Europe/Paris", + aggregator: "eval", + timeRange: "Week", + groupByFieldName: "`ls`", + }, user) + }.to raise_error(ForestLiana::Errors::HTTP422Error, 'Invalid aggregate function') + end + end + describe 'Check client_timezone function' do describe 'with a SQLite database' do it 'should return false' do diff --git a/spec/services/forest_liana/pie_stat_getter_spec.rb b/spec/services/forest_liana/pie_stat_getter_spec.rb index 5118bf8d..6fe8f1d6 100644 --- a/spec/services/forest_liana/pie_stat_getter_spec.rb +++ b/spec/services/forest_liana/pie_stat_getter_spec.rb @@ -23,90 +23,113 @@ module ForestLiana } end - let(:model) { Tree } - let(:collection) { 'trees' } - let(:params) { - { - type: 'Pie', - sourceCollectionName: collection, - timezone: 'Europe/Paris', - aggregator: 'Count', - groupByFieldName: groupByFieldName + describe 'with not allowed aggregator' do + let(:scopes) { { } } + let(:model) { Tree } + let(:collection) { 'trees' } + let(:params) { + { + type: 'Pie', + sourceCollectionName: collection, + timezone: 'Europe/Paris', + aggregator: 'eval', + groupByFieldName: '`ls`' + } } - } - subject { PieStatGetter.new(model, params, user) } + it 'should raise an error' do + expect { + PieStatGetter.new(model, params, user) + }.to raise_error(ForestLiana::Errors::HTTP422Error, 'Invalid aggregate function') + end + end - describe 'with empty scopes' do - let(:scopes) { { } } + describe 'with valid aggregate function' do + let(:model) { Tree } + let(:collection) { 'trees' } + let(:params) { + { + type: 'Pie', + sourceCollectionName: collection, + timezone: 'Europe/Paris', + aggregator: 'Count', + groupByFieldName: groupByFieldName + } + } - describe 'with an aggregate on the name field' do - let(:groupByFieldName) { 'name' } - - it 'should be as many categories as records count' do - subject.perform - expect(subject.record.value).to match_array([ - {:key => "Old Tree n1", :value => 1}, - {:key => "Old Tree n2", :value => 1}, - {:key => "Old Tree n3", :value => 1}, - {:key => "Old Tree n4", :value => 1}, - {:key => "Young Tree n1", :value => 1}, - {:key => "Young Tree n2", :value => 1}, - {:key => "Young Tree n3", :value => 1}, - {:key => "Young Tree n4", :value => 1}, - {:key => "Young Tree n5", :value => 1} - ]) + subject { PieStatGetter.new(model, params, user) } + + describe 'with empty scopes' do + let(:scopes) { { } } + + describe 'with an aggregate on the name field' do + let(:groupByFieldName) { 'name' } + + it 'should be as many categories as records count' do + subject.perform + expect(subject.record.value).to match_array([ + {:key => "Old Tree n1", :value => 1}, + {:key => "Old Tree n2", :value => 1}, + {:key => "Old Tree n3", :value => 1}, + {:key => "Old Tree n4", :value => 1}, + {:key => "Young Tree n1", :value => 1}, + {:key => "Young Tree n2", :value => 1}, + {:key => "Young Tree n3", :value => 1}, + {:key => "Young Tree n4", :value => 1}, + {:key => "Young Tree n5", :value => 1} + ]) + end end - end - describe 'with an aggregate on the age field' do - let(:groupByFieldName) { 'age' } + describe 'with an aggregate on the age field' do + let(:groupByFieldName) { 'age' } - it 'should be as many categories as different ages among records' do - subject.perform - expect(subject.record.value).to eq [{ :key => 3, :value => 5}, { :key => 15, :value => 4 }] + it 'should be as many categories as different ages among records' do + subject.perform + expect(subject.record.value).to eq [{ :key => 3, :value => 5}, { :key => 15, :value => 4 }] + end end end - end - describe 'with scopes' do - let(:scopes) { - { - 'Tree' => { - 'scope'=> { - 'filter'=> { - 'aggregator' => 'and', - 'conditions' => [ - { 'field' => 'age', 'operator' => 'less_than', 'value' => 10 } - ] - }, - 'dynamicScopesValues' => { } + describe 'with scopes' do + let(:scopes) { + { + 'Tree' => { + 'scope'=> { + 'filter'=> { + 'aggregator' => 'and', + 'conditions' => [ + { 'field' => 'age', 'operator' => 'less_than', 'value' => 10 } + ] + }, + 'dynamicScopesValues' => { } + } } } } - } - describe 'with an aggregate on the name field' do - let(:groupByFieldName) { 'name' } - - it 'should be as many categories as records inside the scope' do - subject.perform - expect(subject.record.value).to match_array([ - {:key => "Young Tree n1", :value => 1}, - {:key => "Young Tree n2", :value => 1}, - {:key => "Young Tree n3", :value => 1}, - {:key => "Young Tree n4", :value => 1}, - {:key => "Young Tree n5", :value => 1} - ]) + describe 'with an aggregate on the name field' do + let(:groupByFieldName) { 'name' } + + it 'should be as many categories as records inside the scope' do + subject.perform + expect(subject.record.value).to match_array([ + {:key => "Young Tree n1", :value => 1}, + {:key => "Young Tree n2", :value => 1}, + {:key => "Young Tree n3", :value => 1}, + {:key => "Young Tree n4", :value => 1}, + {:key => "Young Tree n5", :value => 1} + ]) + end end - end - describe 'with an aggregate on the age field' do - let(:groupByFieldName) { 'age' } + describe 'with an aggregate on the age field' do + let(:groupByFieldName) { 'age' } - it 'should be only one category' do - subject.perform - expect(subject.record.value).to eq [{ :key => 3, :value => 5}] + it 'should be only one category' do + subject.perform + expect(subject.record.value).to eq [{ :key => 3, :value => 5}] + end end end end diff --git a/spec/services/forest_liana/value_stat_getter_spec.rb b/spec/services/forest_liana/value_stat_getter_spec.rb index 5f46a83e..7a27babf 100644 --- a/spec/services/forest_liana/value_stat_getter_spec.rb +++ b/spec/services/forest_liana/value_stat_getter_spec.rb @@ -15,80 +15,103 @@ module ForestLiana Tree.create!(name: 'Tree n3', age: 4, island: island, owner: king, cutter: villager) end - let(:params) { - { - type: "Value", - sourceCollectionName: sourceCollectionName, - timezone: "Europe/Paris", - aggregator: "Count", - filter: filter + describe 'with not allowed aggregator' do + let(:model) { User } + let(:collection) { 'users' } + let(:scopes) { { } } + let(:params) { + { + type: "Value", + sourceCollectionName: collection, + timezone: "Europe/Paris", + aggregator: "eval", + aggregateFieldName: "`ls`" + } } - } - subject { ValueStatGetter.new(model, params, user) } + it 'should raise an error' do + expect { + ValueStatGetter.new(model, params, user) + }.to raise_error(ForestLiana::Errors::HTTP422Error, 'Invalid aggregate function') + end + end - describe 'with empty scopes' do - let(:scopes) { { } } + describe 'with valid aggregate function' do + let(:params) { + { + type: "Value", + sourceCollectionName: sourceCollectionName, + timezone: "Europe/Paris", + aggregator: "Count", + filter: filter + } + } + + subject { ValueStatGetter.new(model, params, user) } - describe 'with a simple filter matching no entries' do - let(:model) { User } - let(:sourceCollectionName) { 'users' } - let(:filter) { { field: 'name', operator: 'in', value: ['Merry', 'Pippin'] }.to_json } + describe 'with empty scopes' do + let(:scopes) { { } } - it 'should have a countCurrent of 0' do - subject.perform - expect(subject.record.value[:countCurrent]).to eq 0 + describe 'with a simple filter matching no entries' do + let(:model) { User } + let(:sourceCollectionName) { 'users' } + let(:filter) { { field: 'name', operator: 'in', value: ['Merry', 'Pippin'] }.to_json } + + it 'should have a countCurrent of 0' do + subject.perform + expect(subject.record.value[:countCurrent]).to eq 0 + end end - end - describe 'with a filter on a belongs_to string field' do - let(:model) { Tree } - let(:sourceCollectionName) { 'trees' } - let(:filter) { { field: 'owner:name', operator: 'equal', value: 'Aragorn' }.to_json } + describe 'with a filter on a belongs_to string field' do + let(:model) { Tree } + let(:sourceCollectionName) { 'trees' } + let(:filter) { { field: 'owner:name', operator: 'equal', value: 'Aragorn' }.to_json } - it 'should have a countCurrent of 2' do - subject.perform - expect(subject.record.value[:countCurrent]).to eq 2 + it 'should have a countCurrent of 2' do + subject.perform + expect(subject.record.value[:countCurrent]).to eq 2 + end end - end - describe 'with a filter on a belongs_to enum field' do - let(:model) { Tree } - let(:sourceCollectionName) { 'trees' } - let(:filter) { { field: 'owner:title', operator: 'equal', value: 'villager' }.to_json } + describe 'with a filter on a belongs_to enum field' do + let(:model) { Tree } + let(:sourceCollectionName) { 'trees' } + let(:filter) { { field: 'owner:title', operator: 'equal', value: 'villager' }.to_json } - it 'should have a countCurrent of 1' do - subject.perform - expect(subject.record.value[:countCurrent]).to eq 1 + it 'should have a countCurrent of 1' do + subject.perform + expect(subject.record.value[:countCurrent]).to eq 1 + end end end - end - describe 'with scopes' do - let(:scopes) { - { - 'User' => { - 'scope'=> { - 'filter'=> { - 'aggregator' => 'and', - 'conditions' => [ - { 'field' => 'title', 'operator' => 'not_equal', 'value' => 'villager' } - ] - }, - 'dynamicScopesValues' => { } + describe 'with scopes' do + let(:scopes) { + { + 'User' => { + 'scope'=> { + 'filter'=> { + 'aggregator' => 'and', + 'conditions' => [ + { 'field' => 'title', 'operator' => 'not_equal', 'value' => 'villager' } + ] + }, + 'dynamicScopesValues' => { } + } } } } - } - describe 'with a filter on a belongs_to enum field' do - let(:model) { User } - let(:sourceCollectionName) { 'users' } - let(:filter) { { field: 'title', operator: 'equal', value: 'villager' }.to_json } + describe 'with a filter on a belongs_to enum field' do + let(:model) { User } + let(:sourceCollectionName) { 'users' } + let(:filter) { { field: 'title', operator: 'equal', value: 'villager' }.to_json } - it 'should have a countCurrent of 0' do - subject.perform - expect(subject.record.value[:countCurrent]).to eq 0 + it 'should have a countCurrent of 0' do + subject.perform + expect(subject.record.value[:countCurrent]).to eq 0 + end end end end From e7832d43bb5ea2367f762b3902ca57858e852821 Mon Sep 17 00:00:00 2001 From: Forest Bot Date: Tue, 5 Dec 2023 09:29:44 +0000 Subject: [PATCH 34/36] chore(release): 8.0.17 [skip ci] ## [8.0.17](https://github.com/ForestAdmin/forest-rails/compare/v8.0.16...v8.0.17) (2023-12-05) ### Bug Fixes * security vulnerabilities RCE on 8.x.x ([#638](https://github.com/ForestAdmin/forest-rails/issues/638)) ([02679b5](https://github.com/ForestAdmin/forest-rails/commit/02679b584b84eda3d1f0be6e6482d130b53b4d19)) --- CHANGELOG.md | 7 +++++++ Gemfile.lock | 2 +- lib/forest_liana/version.rb | 2 +- package.json | 2 +- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 96b132d3..2eca7a87 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [8.0.17](https://github.com/ForestAdmin/forest-rails/compare/v8.0.16...v8.0.17) (2023-12-05) + + +### Bug Fixes + +* security vulnerabilities RCE on 8.x.x ([#638](https://github.com/ForestAdmin/forest-rails/issues/638)) ([02679b5](https://github.com/ForestAdmin/forest-rails/commit/02679b584b84eda3d1f0be6e6482d130b53b4d19)) + ## [8.0.16](https://github.com/ForestAdmin/forest-rails/compare/v8.0.15...v8.0.16) (2023-11-16) diff --git a/Gemfile.lock b/Gemfile.lock index 770036e6..aae31433 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - forest_liana (8.0.16) + forest_liana (8.0.17) arel-helpers bcrypt deepsort diff --git a/lib/forest_liana/version.rb b/lib/forest_liana/version.rb index cbc628aa..cfcf077d 100644 --- a/lib/forest_liana/version.rb +++ b/lib/forest_liana/version.rb @@ -1,3 +1,3 @@ module ForestLiana - VERSION = "8.0.16" + VERSION = "8.0.17" end diff --git a/package.json b/package.json index a2defba2..880f2874 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "forest-rails", - "version": "8.0.16", + "version": "8.0.17", "description": "The official Rails liana for Forest.", "directories": { "test": "test" From 2d43bc35c9085555cb3625a4325e427e80a5ec6a Mon Sep 17 00:00:00 2001 From: NicolasAlexandre Date: Thu, 18 Jan 2024 16:13:45 +0100 Subject: [PATCH 35/36] feat: add polymorphic associations support (#640) --- .../forest_liana/resource_deserializer.rb | 7 +- app/helpers/forest_liana/query_helper.rb | 27 ++++-- .../forest_liana/serializer_factory.rb | 18 +++- app/services/forest_liana/apimap_sorter.rb | 1 + app/services/forest_liana/base_getter.rb | 19 +++-- .../forest_liana/belongs_to_updater.rb | 11 ++- app/services/forest_liana/has_many_getter.rb | 22 +++-- app/services/forest_liana/schema_adapter.rb | 38 ++++++++- app/services/forest_liana/schema_utils.rb | 29 ++++++- lib/forest_liana/bootstrapper.rb | 7 +- lib/forest_liana/schema_file_updater.rb | 1 + lib/tasks/send_apimap.rake | 2 +- spec/dummy/app/models/address.rb | 5 ++ spec/dummy/app/models/user.rb | 1 + .../20231117084236_create_addresses.rb | 12 +++ spec/dummy/db/schema.rb | 13 ++- .../helpers/forest_liana/query_helper_spec.rb | 26 +++++- spec/lib/forest_liana/bootstrapper_spec.rb | 1 + .../forest_liana/schema_file_updater_spec.rb | 35 ++++++++ spec/requests/resources_spec.rb | 84 +++++++++++++++++++ .../forest_liana/schema_adapter_spec.rb | 58 +++++++++++++ spec/spec_helper.rb | 2 + 22 files changed, 389 insertions(+), 30 deletions(-) create mode 100644 spec/dummy/app/models/address.rb create mode 100644 spec/dummy/db/migrate/20231117084236_create_addresses.rb diff --git a/app/deserializers/forest_liana/resource_deserializer.rb b/app/deserializers/forest_liana/resource_deserializer.rb index daf3a46a..83e19871 100644 --- a/app/deserializers/forest_liana/resource_deserializer.rb +++ b/app/deserializers/forest_liana/resource_deserializer.rb @@ -63,7 +63,12 @@ def extract_relationships # ActionController::Parameters do not inherit from Hash anymore # since Rails 5. if (data.is_a?(Hash) || data.is_a?(ActionController::Parameters)) && data[:id] - @attributes[name] = association.klass.find(data[:id]) + if (SchemaUtils.polymorphic?(association)) + @attributes[association.foreign_key] = data[:id] + @attributes[association.foreign_type] = data[:type] + else + @attributes[name] = association.klass.find(data[:id]) + end elsif data.blank? @attributes[name] = nil end diff --git a/app/helpers/forest_liana/query_helper.rb b/app/helpers/forest_liana/query_helper.rb index ce359775..87e410f0 100644 --- a/app/helpers/forest_liana/query_helper.rb +++ b/app/helpers/forest_liana/query_helper.rb @@ -1,8 +1,16 @@ module ForestLiana module QueryHelper def self.get_one_associations(resource) - SchemaUtils.one_associations(resource) - .select { |association| SchemaUtils.model_included?(association.klass) } + associations = SchemaUtils.one_associations(resource) + .select do |association| + if SchemaUtils.polymorphic?(association) + SchemaUtils.polymorphic_models(association).all? { |model| SchemaUtils.model_included?(model) } + else + SchemaUtils.model_included?(association.klass) + end + end + + associations end def self.get_one_association_names_symbol(resource) @@ -18,10 +26,19 @@ def self.get_tables_associated_to_relations_name(resource) associations_has_one = self.get_one_associations(resource) associations_has_one.each do |association| - if tables_associated_to_relations_name[association.table_name].nil? - tables_associated_to_relations_name[association.table_name] = [] + if SchemaUtils.polymorphic?(association) + SchemaUtils.polymorphic_models(association).each do |model| + if tables_associated_to_relations_name[model.table_name].nil? + tables_associated_to_relations_name[model.table_name] = [] + end + tables_associated_to_relations_name[model.table_name] << association.name + end + else + if tables_associated_to_relations_name[association.try(:table_name)].nil? + tables_associated_to_relations_name[association.table_name] = [] + end + tables_associated_to_relations_name[association.table_name] << association.name end - tables_associated_to_relations_name[association.table_name] << association.name end tables_associated_to_relations_name diff --git a/app/serializers/forest_liana/serializer_factory.rb b/app/serializers/forest_liana/serializer_factory.rb index e630e7f6..a06f88de 100644 --- a/app/serializers/forest_liana/serializer_factory.rb +++ b/app/serializers/forest_liana/serializer_factory.rb @@ -265,7 +265,19 @@ def mixpanel_integration? SchemaUtils.associations(active_record_class).each do |a| begin - if SchemaUtils.model_included?(a.klass) + if SchemaUtils.polymorphic?(a) + serializer.send(serializer_association(a), a.name) { + if [:has_one, :belongs_to].include?(a.macro) + begin + object.send(a.name) + rescue ActiveRecord::RecordNotFound + nil + end + else + [] + end + } + elsif SchemaUtils.model_included?(a.klass) serializer.send(serializer_association(a), a.name) { if [:has_one, :belongs_to].include?(a.macro) begin @@ -369,6 +381,7 @@ def serializer_association(association) def attributes(active_record_class) return [] if @is_smart_collection + active_record_class.column_names.select do |column_name| !association?(active_record_class, column_name) end @@ -410,6 +423,9 @@ def association?(active_record_class, column_name) def foreign_keys(active_record_class) begin SchemaUtils.belongs_to_associations(active_record_class).map(&:foreign_key) + SchemaUtils.belongs_to_associations(active_record_class) + .select { |association| !SchemaUtils.polymorphic?(association) } + .map(&:foreign_key) rescue => err # Association foreign_key triggers an error. Put the stacktrace and # returns no foreign keys. diff --git a/app/services/forest_liana/apimap_sorter.rb b/app/services/forest_liana/apimap_sorter.rb index 3bcffb8a..6206347c 100644 --- a/app/services/forest_liana/apimap_sorter.rb +++ b/app/services/forest_liana/apimap_sorter.rb @@ -29,6 +29,7 @@ class ApimapSorter 'relationship', 'widget', 'validations', + 'polymorphic_referenced_models', ] KEYS_ACTION = [ 'name', diff --git a/app/services/forest_liana/base_getter.rb b/app/services/forest_liana/base_getter.rb index 569922b8..604249f7 100644 --- a/app/services/forest_liana/base_getter.rb +++ b/app/services/forest_liana/base_getter.rb @@ -33,16 +33,23 @@ def compute_includes def optimize_record_loading(resource, records) instance_dependent_associations = instance_dependent_associations(resource) + polymorphic = [] preload_loads = @includes.select do |name| - targetModelConnection = resource.reflect_on_association(name).klass.connection - targetModelDatabase = targetModelConnection.current_database if targetModelConnection.respond_to? :current_database - resourceConnection = resource.connection - resourceDatabase = resourceConnection.current_database if resourceConnection.respond_to? :current_database + association = resource.reflect_on_association(name) + if SchemaUtils.polymorphic?(association) + polymorphic << association.name + false + else + targetModelConnection = association.klass.connection + targetModelDatabase = targetModelConnection.current_database if targetModelConnection.respond_to? :current_database + resourceConnection = resource.connection + resourceDatabase = resourceConnection.current_database if resourceConnection.respond_to? :current_database - targetModelDatabase != resourceDatabase + targetModelDatabase != resourceDatabase + end end + instance_dependent_associations - result = records.eager_load(@includes - preload_loads) + result = records.eager_load(@includes - preload_loads - polymorphic) # Rails 7 can mix `eager_load` and `preload` in the same scope # Rails 6 cannot mix `eager_load` and `preload` in the same scope diff --git a/app/services/forest_liana/belongs_to_updater.rb b/app/services/forest_liana/belongs_to_updater.rb index e36ee2bd..eef84d1a 100644 --- a/app/services/forest_liana/belongs_to_updater.rb +++ b/app/services/forest_liana/belongs_to_updater.rb @@ -13,7 +13,16 @@ def initialize(resource, association, params) def perform begin @record = @resource.find(@params[:id]) - new_value = @association.klass.find(@data[:id]) if @data && @data[:id] + if (SchemaUtils.polymorphic?(@association)) + if @data.nil? + new_value = nil + else + association_klass = SchemaUtils.polymorphic_models(@association).select { |a| a.name.downcase == @data[:type] }.first + new_value = association_klass.find(@data[:id]) if @data && @data[:id] + end + else + new_value = @association.klass.find(@data[:id]) if @data && @data[:id] + end @record.send("#{@association.name}=", new_value) @record.save diff --git a/app/services/forest_liana/has_many_getter.rb b/app/services/forest_liana/has_many_getter.rb index da49648f..0c5e0e19 100644 --- a/app/services/forest_liana/has_many_getter.rb +++ b/app/services/forest_liana/has_many_getter.rb @@ -40,17 +40,23 @@ def compute_includes @includes = @association.klass .reflect_on_all_associations .select do |association| - inclusion = !association.options[:polymorphic] && - SchemaUtils.model_included?(association.klass) && - [:belongs_to, :has_and_belongs_to_many].include?(association.macro) - if @field_names_requested - inclusion && @field_names_requested.include?(association.name) + if SchemaUtils.polymorphic?(association) + inclusion = SchemaUtils.polymorphic_models(association) + .all? { |model| SchemaUtils.model_included?(model) } && + [:belongs_to, :has_and_belongs_to_many].include?(association.macro) else - inclusion + inclusion = SchemaUtils.model_included?(association.klass) && + [:belongs_to, :has_and_belongs_to_many].include?(association.macro) end - end - .map { |association| association.name.to_s } + + if @field_names_requested + inclusion && @field_names_requested.include?(association.name) + else + inclusion + end + end + .map { |association| association.name } end def field_names_requested diff --git a/app/services/forest_liana/schema_adapter.rb b/app/services/forest_liana/schema_adapter.rb index afaad012..e7489bec 100644 --- a/app/services/forest_liana/schema_adapter.rb +++ b/app/services/forest_liana/schema_adapter.rb @@ -241,8 +241,33 @@ def add_columns def add_associations SchemaUtils.associations(@model).each do |association| begin + if SchemaUtils.polymorphic?(association) && + (ENV['ENABLE_SUPPORT_POLYMORPHISM'].present? && ENV['ENABLE_SUPPORT_POLYMORPHISM'].downcase == 'true') + + collection.fields << { + field: association.name.to_s, + type: get_type_for_association(association), + relationship: get_relationship_type(association), + reference: "#{association.name.to_s}.id", + inverse_of: @model.name.demodulize.underscore, + is_filterable: false, + is_sortable: true, + is_read_only: false, + is_required: false, + is_virtual: false, + default_value: nil, + integration: nil, + relationships: nil, + widget: nil, + validations: [], + polymorphic_referenced_models: get_polymorphic_types(association) + } + + collection.fields = collection.fields.reject do |field| + field[:field] == association.foreign_key || field[:field] == association.foreign_type + end # NOTICE: Delete the association if the targeted model is excluded. - if !SchemaUtils.model_included?(association.klass) + elsif !SchemaUtils.model_included?(association.klass) field = collection.fields.find do |x| x[:field] == association.foreign_key end @@ -275,6 +300,17 @@ def inverse_of(association) automatic_inverse_of(association) end + def get_polymorphic_types(relation) + types = [] + ForestLiana.models.each do |model| + unless model.reflect_on_all_associations.select { |association| association.options[:as] == relation.name.to_sym }.empty? + types << model.name + end + end + + types + end + def automatic_inverse_of(association) name = association.active_record.name.demodulize.underscore diff --git a/app/services/forest_liana/schema_utils.rb b/app/services/forest_liana/schema_utils.rb index e5966237..c9766ac9 100644 --- a/app/services/forest_liana/schema_utils.rb +++ b/app/services/forest_liana/schema_utils.rb @@ -4,7 +4,12 @@ class SchemaUtils def self.associations(active_record_class) active_record_class.reflect_on_all_associations.select do |association| begin - !polymorphic?(association) && !is_active_type?(association.klass) + if (ENV['ENABLE_SUPPORT_POLYMORPHISM'].present? && ENV['ENABLE_SUPPORT_POLYMORPHISM'].downcase == 'true') + polymorphic?(association) ? true : !is_active_type?(association.klass) + else + !polymorphic?(association) && !is_active_type?(association.klass) + end + rescue FOREST_LOGGER.warn "Unknown association #{association.name} on class #{active_record_class.name}" false @@ -53,12 +58,30 @@ def self.tables_names ActiveRecord::Base.connection.tables end - private - def self.polymorphic?(association) association.options[:polymorphic] end + def self.klass(association) + return association.klass unless polymorphic?(association) + + + end + + def self.polymorphic_models(relation) + models = [] + ForestLiana.models.each do |model| + unless model.reflect_on_all_associations.select { |association| association.options[:as] == relation.name.to_sym }.empty? + models << model + end + end + + models + end + + + private + def self.find_model_from_abstract_class(abstract_class, collection_name) abstract_class.subclasses.find do |subclass| if subclass.abstract_class? diff --git a/lib/forest_liana/bootstrapper.rb b/lib/forest_liana/bootstrapper.rb index 0b0a45ed..2115554f 100644 --- a/lib/forest_liana/bootstrapper.rb +++ b/lib/forest_liana/bootstrapper.rb @@ -5,7 +5,12 @@ module ForestLiana class Bootstrapper SCHEMA_FILENAME = File.join(Dir.pwd, '.forestadmin-schema.json') - def initialize + def initialize(reset_api_map = false) + if reset_api_map + ForestLiana.apimap = [] + ForestLiana.models = [] + end + @integration_stripe_valid = false @integration_intercom_valid = false diff --git a/lib/forest_liana/schema_file_updater.rb b/lib/forest_liana/schema_file_updater.rb index 9ffd606e..0e322486 100644 --- a/lib/forest_liana/schema_file_updater.rb +++ b/lib/forest_liana/schema_file_updater.rb @@ -35,6 +35,7 @@ class SchemaFileUpdater 'relationship', 'widget', 'validations', + 'polymorphic_referenced_models', ] KEYS_VALIDATION = [ 'message', diff --git a/lib/tasks/send_apimap.rake b/lib/tasks/send_apimap.rake index 0ccd8d99..e722bf5f 100644 --- a/lib/tasks/send_apimap.rake +++ b/lib/tasks/send_apimap.rake @@ -3,7 +3,7 @@ namespace :forest do task(:send_apimap).clear task send_apimap: :environment do if ForestLiana.env_secret - bootstrapper = ForestLiana::Bootstrapper.new + bootstrapper = ForestLiana::Bootstrapper.new(true) bootstrapper.synchronize(true) else puts 'Cannot send the Apimap, Forest cannot find your env_secret' diff --git a/spec/dummy/app/models/address.rb b/spec/dummy/app/models/address.rb new file mode 100644 index 00000000..6d62b71f --- /dev/null +++ b/spec/dummy/app/models/address.rb @@ -0,0 +1,5 @@ +class Address < ActiveRecord::Base + self.table_name = 'addresses' + + belongs_to :addressable, polymorphic: true +end diff --git a/spec/dummy/app/models/user.rb b/spec/dummy/app/models/user.rb index 751c4a30..c90ff80f 100644 --- a/spec/dummy/app/models/user.rb +++ b/spec/dummy/app/models/user.rb @@ -1,6 +1,7 @@ class User < ActiveRecord::Base has_many :trees_owned, class_name: 'Tree', inverse_of: :owner has_many :trees_cut, class_name: 'Tree', inverse_of: :cutter + has_many :addresses, as: :addressable enum title: [ :king, :villager, :outlaw ] end diff --git a/spec/dummy/db/migrate/20231117084236_create_addresses.rb b/spec/dummy/db/migrate/20231117084236_create_addresses.rb new file mode 100644 index 00000000..7e75b7db --- /dev/null +++ b/spec/dummy/db/migrate/20231117084236_create_addresses.rb @@ -0,0 +1,12 @@ +class CreateAddresses < ActiveRecord::Migration[6.0] + def change + create_table :addresses do |t| + t.string :line1 + t.string :city + t.string :zipcode + t.references :addressable, polymorphic: true, null: false + + t.timestamps + end + end +end diff --git a/spec/dummy/db/schema.rb b/spec/dummy/db/schema.rb index 49b25e92..81fb8816 100644 --- a/spec/dummy/db/schema.rb +++ b/spec/dummy/db/schema.rb @@ -10,7 +10,18 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2022_07_27_114930) do +ActiveRecord::Schema.define(version: 2023_11_17_084236) do + + create_table "addresses", force: :cascade do |t| + t.string "line1" + t.string "city" + t.string "zipcode" + t.string "addressable_type", null: false + t.integer "addressable_id", null: false + t.datetime "created_at", precision: 6, null: false + t.datetime "updated_at", precision: 6, null: false + t.index ["addressable_type", "addressable_id"], name: "index_addresses_on_addressable_type_and_addressable_id" + end create_table "isle", force: :cascade do |t| t.string "name" diff --git a/spec/helpers/forest_liana/query_helper_spec.rb b/spec/helpers/forest_liana/query_helper_spec.rb index e701a077..21d77b38 100644 --- a/spec/helpers/forest_liana/query_helper_spec.rb +++ b/spec/helpers/forest_liana/query_helper_spec.rb @@ -3,6 +3,7 @@ module ForestLiana before(:all) do Tree.connection User.connection + Address.connection Island.connection end @@ -14,6 +15,21 @@ module ForestLiana end end + context 'on a model having 1 polymorphic association' do + it 'should return the association' do + associations = QueryHelper.get_one_associations(Address) + expect(associations).to eq(Address.reflect_on_all_associations(:belongs_to)) + end + + it 'should return 0 association when one of referenced model was excluded' do + allow(ForestLiana).to receive(:excluded_models).and_return(['User']) + associations = QueryHelper.get_one_associations(Address) + + expect(associations.length).to eq(0) + end + end + + context 'on a model having some belongsTo associations' do let(:expected_association_attributes) do [ @@ -75,7 +91,15 @@ module ForestLiana expect(tables_associated_to_relations_name['isle'].second).to eq(:eponymous_island) end end - end + context 'on a model having polymorphic association' do + tables_associated_to_relations_name = + QueryHelper.get_tables_associated_to_relations_name(Address) + + it 'should return the one-one associations' do + expect(tables_associated_to_relations_name.keys.length).to eq(1) + end + end + end end end diff --git a/spec/lib/forest_liana/bootstrapper_spec.rb b/spec/lib/forest_liana/bootstrapper_spec.rb index 1cb2bea6..752036ad 100644 --- a/spec/lib/forest_liana/bootstrapper_spec.rb +++ b/spec/lib/forest_liana/bootstrapper_spec.rb @@ -23,6 +23,7 @@ module ForestLiana let(:expected_application_models) do [ + Address, Island, Location, Manufacturer, diff --git a/spec/lib/forest_liana/schema_file_updater_spec.rb b/spec/lib/forest_liana/schema_file_updater_spec.rb index 546a8ff9..0ebe0bb8 100644 --- a/spec/lib/forest_liana/schema_file_updater_spec.rb +++ b/spec/lib/forest_liana/schema_file_updater_spec.rb @@ -10,6 +10,41 @@ module ForestLiana end describe "with a given collection" do + describe "when the collection has a polymorphic relation" do + it "should save the relation" do + collections = [ + { + "name" => "Address", + "fields" => [ + { + "field" => "addressable", + "type" => "Number", + "relationship" => "BelongsTo", + "reference" => "addressable.id", + "inverse_of" => "address", + "is_filterable" => false, + "is_sortable" => true, + "is_read_only" => false, + "is_required" => false, + "is_virtual" => false, + "default_value" => nil, + "integration" => nil, + "relationships" => nil, + "widget" => nil, + "validations" => [], + "polymorphic_referenced_models" => ["User"] + }, + ], + "actions" => [], + "segments" => [] + } + ] + schema_file_updater = ForestLiana::SchemaFileUpdater.new("test.txt", collections, {}) + expect(schema_file_updater.instance_variable_get(:@collections)) + .to eq(collections) + end + end + describe "when the collection has a smart action action" do it "should save the smart action" do collections = [{ diff --git a/spec/requests/resources_spec.rb b/spec/requests/resources_spec.rb index 36828ca1..872082b9 100644 --- a/spec/requests/resources_spec.rb +++ b/spec/requests/resources_spec.rb @@ -154,3 +154,87 @@ end end end + +describe 'Requesting Address resources', :type => :request do + + before do + user = User.create(name: 'Michel') + address = Address.create(line1: '10 Downing Street', city: 'London', zipcode: '2AB', addressable: user) + + Rails.cache.write('forest.users', {'1' => { 'id' => 1, 'roleId' => 1, 'rendering_id' => '1' }}) + Rails.cache.write('forest.has_permission', true) + Rails.cache.write( + 'forest.collections', + { + 'Address' => { + 'browse' => [1], + 'read' => [1], + 'edit' => [1], + 'add' => [1], + 'delete' => [1], + 'export' => [1], + 'actions' => {} + } + } + ) + + allow(ForestLiana::IpWhitelist).to receive(:retrieve) { true } + allow(ForestLiana::IpWhitelist).to receive(:is_ip_whitelist_retrieved) { true } + allow(ForestLiana::IpWhitelist).to receive(:is_ip_valid) { true } + allow(ForestLiana::ScopeManager).to receive(:fetch_scopes).and_return({}) + end + + after do + User.destroy_all + Address.destroy_all + end + + token = JWT.encode({ + id: 1, + email: 'michael.kelso@that70.show', + first_name: 'Michael', + last_name: 'Kelso', + team: 'Operations', + rendering_id: 16, + exp: Time.now.to_i + 2.weeks.to_i, + permission_level: 'admin' + }, ForestLiana.auth_secret, 'HS256') + + headers = { + 'Accept' => 'application/json', + 'Content-Type' => 'application/json', + 'Authorization' => "Bearer #{token}" + } + + describe 'index' do + params = { + fields: { 'Address' => 'id,line1,city,zip_code,addressable' }, + page: { 'number' => '1', 'size' => '10' }, + searchExtended: '0', + sort: '-id', + timezone: 'Europe/Paris' + } + + it 'should respond the address data' do + get '/forest/Address', params: params, headers: headers + + expect(JSON.parse(response.body)).to include( + "data" => [ + { + "type" => "Address", + "id" => "1", + "attributes" => { + "id" => 1, + "line1" => "10 Downing Street", + "city" => "London" + }, + "links" => { "self" => "/forest/address/1" }, + "relationships" => { + "addressable" => { "links" => { "related" => {} }, "data" => { "type" => "User", "id" => "1" } } + } + } + ] + ) + end + end +end diff --git a/spec/services/forest_liana/schema_adapter_spec.rb b/spec/services/forest_liana/schema_adapter_spec.rb index 57339fc1..2263974d 100644 --- a/spec/services/forest_liana/schema_adapter_spec.rb +++ b/spec/services/forest_liana/schema_adapter_spec.rb @@ -1,6 +1,64 @@ module ForestLiana describe SchemaAdapter do describe 'perform' do + context 'with polymorphic association' do + it 'should define the association with the referenced models' do + collection = ForestLiana.apimap.find do |object| + object.name.to_s == ForestLiana.name_for(Address) + end + field = collection.fields.find { |field| field[:field] == 'addressable' } + + expect(field).to eq( + { + field: "addressable", + type: "Number", + relationship: "BelongsTo", + reference: "addressable.id", + inverse_of: "address", + is_filterable: false, + is_sortable: true, + is_read_only: false, + is_required: false, + is_virtual: false, + default_value: nil, + integration: nil, + relationships: nil, + widget: nil, + validations: [], + polymorphic_referenced_models: ['User'] + } + ) + end + + it 'should remove the polymorphic attributes(_id and _type)' do + collection = ForestLiana.apimap.find do |object| + object.name.to_s == ForestLiana.name_for(Address) + end + removed_fields = collection.fields.select do + |field| field[:field] == 'addressable_id' || field[:field] == 'addressable_type' + end + + expect(removed_fields).to be_empty + end + + context 'when the polymorphic support was disabled' do + it 'should not define the association' do + ENV['ENABLE_SUPPORT_POLYMORPHISM'] = 'false' + Bootstrapper.new(true) + collection = ForestLiana.apimap.find do |object| + object.name.to_s == ForestLiana.name_for(Address) + end + association = collection.fields.find { |field| field[:field] == 'addressable' } + fields = collection.fields.select do |field| + field[:field] == 'addressable_id' || field[:field] == 'addressable_type' + end + + expect(association).to be_nil + expect(fields.size).to eq(2) + end + end + end + context 'with an "unhandled" column types (binary, postgis geography, ...)' do it 'should not define theses column in the schema' do collection = ForestLiana.apimap.find do |object| diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index d5118aa2..65ef2563 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -19,6 +19,8 @@ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration RSpec.configure do |config| ENV['RAILS_ENV'] = 'test' + ENV['ENABLE_SUPPORT_POLYMORPHISM'] = 'true' + require File.expand_path('../dummy/config/environment', __FILE__) # rspec-expectations config goes here. You can use an alternate From 1a73eb6e709a4f60c2f76b49a3cc86188eb7669e Mon Sep 17 00:00:00 2001 From: Forest Bot Date: Thu, 18 Jan 2024 15:15:54 +0000 Subject: [PATCH 36/36] chore(release): 8.1.0 [skip ci] # [8.1.0](https://github.com/ForestAdmin/forest-rails/compare/v8.0.17...v8.1.0) (2024-01-18) ### Features * add polymorphic associations support ([#640](https://github.com/ForestAdmin/forest-rails/issues/640)) ([2d43bc3](https://github.com/ForestAdmin/forest-rails/commit/2d43bc35c9085555cb3625a4325e427e80a5ec6a)) --- CHANGELOG.md | 7 +++++++ Gemfile.lock | 2 +- lib/forest_liana/version.rb | 2 +- package.json | 2 +- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2eca7a87..f847ca7b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# [8.1.0](https://github.com/ForestAdmin/forest-rails/compare/v8.0.17...v8.1.0) (2024-01-18) + + +### Features + +* add polymorphic associations support ([#640](https://github.com/ForestAdmin/forest-rails/issues/640)) ([2d43bc3](https://github.com/ForestAdmin/forest-rails/commit/2d43bc35c9085555cb3625a4325e427e80a5ec6a)) + ## [8.0.17](https://github.com/ForestAdmin/forest-rails/compare/v8.0.16...v8.0.17) (2023-12-05) diff --git a/Gemfile.lock b/Gemfile.lock index aae31433..96e70eac 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - forest_liana (8.0.17) + forest_liana (8.1.0) arel-helpers bcrypt deepsort diff --git a/lib/forest_liana/version.rb b/lib/forest_liana/version.rb index cfcf077d..3329d49c 100644 --- a/lib/forest_liana/version.rb +++ b/lib/forest_liana/version.rb @@ -1,3 +1,3 @@ module ForestLiana - VERSION = "8.0.17" + VERSION = "8.1.0" end diff --git a/package.json b/package.json index 880f2874..d43ec016 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "forest-rails", - "version": "8.0.17", + "version": "8.1.0", "description": "The official Rails liana for Forest.", "directories": { "test": "test"