From 1776bb4f4c8c965962fc4ca9f4ef52721cc20dab Mon Sep 17 00:00:00 2001 From: Matt Date: Thu, 28 Sep 2023 15:41:09 +0200 Subject: [PATCH 1/9] fix: test on oidc_client_manager --- .../forest_admin_agent/auth/oidc_client_manager_spec.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/forest_admin_agent/spec/lib/forest_admin_agent/auth/oidc_client_manager_spec.rb b/packages/forest_admin_agent/spec/lib/forest_admin_agent/auth/oidc_client_manager_spec.rb index cb0ee9f4e..0b7ca8f5e 100644 --- a/packages/forest_admin_agent/spec/lib/forest_admin_agent/auth/oidc_client_manager_spec.rb +++ b/packages/forest_admin_agent/spec/lib/forest_admin_agent/auth/oidc_client_manager_spec.rb @@ -11,8 +11,13 @@ module Auth let(:faraday_connection) { instance_double(Faraday::Connection) } context 'when then oidc is called and forest api is down' do + let(:response_bad_request) { instance_double(Faraday::Response, status: 500, body: {}) } + let(:oidc_resource) { instance_double(ForestAdminAgent::Auth::OAuth2::OidcConfig::Resource) } + it 'raises an error' do - class_double(ForestAdminAgent::Auth::OAuth2::OidcConfig, discover!: OpenIDConnect::Discovery::DiscoveryFailed) + allow(Faraday::Connection).to receive(:new).and_return(faraday_connection) + allow(faraday_connection).to receive(:get).and_return(response_bad_request) + expect do oidc_client_manager.make_forest_provider :rendering_id end.to raise_error(ForestAdminAgent::Utils::ErrorMessages::SERVER_DOWN) From c3988f41a6013c37c2bcb9e3eaf992becc06ae1a Mon Sep 17 00:00:00 2001 From: Matt Date: Thu, 28 Sep 2023 15:43:49 +0200 Subject: [PATCH 2/9] chore: update git config --- packages/forest_admin_agent/.gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/forest_admin_agent/.gitignore b/packages/forest_admin_agent/.gitignore index 5c249f1e4..2cd89c230 100644 --- a/packages/forest_admin_agent/.gitignore +++ b/packages/forest_admin_agent/.gitignore @@ -6,6 +6,8 @@ /pkg/ /spec/reports/ /tmp/ +/forest_admin + # rspec failure tracking .rspec_status From 324bde4aeef4c41d6b1b2136eb617d4ed267661a Mon Sep 17 00:00:00 2001 From: Matt Date: Thu, 28 Sep 2023 16:45:34 +0200 Subject: [PATCH 3/9] chore: add package json_api_serializers --- packages/forest_admin_agent/forest_admin_agent.gemspec | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/forest_admin_agent/forest_admin_agent.gemspec b/packages/forest_admin_agent/forest_admin_agent.gemspec index c5ad5b4b2..85bdf80d3 100644 --- a/packages/forest_admin_agent/forest_admin_agent.gemspec +++ b/packages/forest_admin_agent/forest_admin_agent.gemspec @@ -35,6 +35,7 @@ admin work on any Ruby application." spec.add_dependency "dry-container", "~> 0.11" spec.add_dependency "lightly", "~> 0.4.0" + spec.add_dependency "jsonapi-serializers", "~> 1.0" spec.add_dependency "jwt", "~> 2.7" spec.add_dependency "mono_logger", "~> 1.1" spec.add_dependency "openid_connect", "~> 2.2" From 8ed637d24434c8789c6aa5aee99aed69c90c8fa3 Mon Sep 17 00:00:00 2001 From: Matt Date: Fri, 29 Sep 2023 14:49:22 +0200 Subject: [PATCH 4/9] fix: rubocop --- .rubocop.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.rubocop.yml b/.rubocop.yml index 8599907af..2976a46cd 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -40,11 +40,23 @@ Metrics/AbcSize: - 'packages/forest_admin_datasource_active_record/lib/forest_admin_datasource_active_record/collection.rb' - 'packages/forest_admin_agent/lib/forest_admin_agent/utils/schema/generator_field.rb' - 'packages/forest_admin_agent/lib/forest_admin_agent/utils/schema/schema_emitter.rb' + - 'packages/forest_admin_agent/lib/forest_admin_agent/serializer/json_api_serializer.rb' Metrics/CyclomaticComplexity: Exclude: - 'packages/forest_admin_agent/lib/forest_admin_agent/auth/oauth2/forest_provider.rb' - 'packages/forest_admin_datasource_active_record/lib/forest_admin_datasource_active_record/parser/validation.rb' + - 'packages/forest_admin_agent/lib/forest_admin_agent/serializer/json_api_serializer.rb' + +Metrics/MethodLength: + Max: 20 + Exclude: + - 'packages/forest_admin_agent/lib/forest_admin_agent/auth/oauth2/forest_provider.rb' + - 'packages/forest_admin_agent/lib/forest_admin_agent/serializer/json_api_serializer.rb' + +Metrics/PerceivedComplexity: + Exclude: + - 'packages/forest_admin_agent/lib/forest_admin_agent/serializer/json_api_serializer.rb' Style/BlockComments: Exclude: From 13daadbf321f1d47579afbe35835d88965d3f857 Mon Sep 17 00:00:00 2001 From: Matt Date: Fri, 29 Sep 2023 14:52:59 +0200 Subject: [PATCH 5/9] fix: rubocop --- .rubocop.yml | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 2976a46cd..d137083fa 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -36,27 +36,22 @@ Metrics/AbcSize: - 'packages/forest_admin_agent/lib/forest_admin_agent/auth/auth_manager.rb' - 'packages/forest_admin_agent/lib/forest_admin_agent/auth/oauth2/forest_provider.rb' - 'packages/forest_admin_agent/lib/forest_admin_agent/auth/oidc_client_manager.rb' - - 'packages/forest_admin_datasource_active_record/lib/forest_admin_datasource_active_record/parser/validation.rb' - - 'packages/forest_admin_datasource_active_record/lib/forest_admin_datasource_active_record/collection.rb' - 'packages/forest_admin_agent/lib/forest_admin_agent/utils/schema/generator_field.rb' - 'packages/forest_admin_agent/lib/forest_admin_agent/utils/schema/schema_emitter.rb' - 'packages/forest_admin_agent/lib/forest_admin_agent/serializer/json_api_serializer.rb' - -Metrics/CyclomaticComplexity: - Exclude: - - 'packages/forest_admin_agent/lib/forest_admin_agent/auth/oauth2/forest_provider.rb' - 'packages/forest_admin_datasource_active_record/lib/forest_admin_datasource_active_record/parser/validation.rb' - - 'packages/forest_admin_agent/lib/forest_admin_agent/serializer/json_api_serializer.rb' + - 'packages/forest_admin_datasource_active_record/lib/forest_admin_datasource_active_record/collection.rb' -Metrics/MethodLength: - Max: 20 +Metrics/CyclomaticComplexity: Exclude: - 'packages/forest_admin_agent/lib/forest_admin_agent/auth/oauth2/forest_provider.rb' - 'packages/forest_admin_agent/lib/forest_admin_agent/serializer/json_api_serializer.rb' + - 'packages/forest_admin_datasource_active_record/lib/forest_admin_datasource_active_record/parser/validation.rb' Metrics/PerceivedComplexity: Exclude: - 'packages/forest_admin_agent/lib/forest_admin_agent/serializer/json_api_serializer.rb' + - 'packages/forest_admin_datasource_active_record/lib/forest_admin_datasource_active_record/parser/validation.rb' Style/BlockComments: Exclude: @@ -176,15 +171,13 @@ Metrics/MethodLength: - 'packages/forest_admin_datasource_active_record/lib/forest_admin_datasource_active_record/collection.rb' - 'packages/forest_admin_datasource_active_record/spec/dummy/**/*' - 'packages/forest_admin_datasource_toolkit/lib/forest_admin_datasource_toolkit/collection.rb' + - 'packages/forest_admin_agent/lib/forest_admin_agent/auth/oauth2/forest_provider.rb' + - 'packages/forest_admin_agent/lib/forest_admin_agent/serializer/json_api_serializer.rb' Metrics/BlockLength: Exclude: - 'packages/forest_admin_datasource_active_record/lib/forest_admin_datasource_active_record/collection.rb' -Metrics/PerceivedComplexity: - Exclude: - - 'packages/forest_admin_datasource_active_record/lib/forest_admin_datasource_active_record/parser/validation.rb' - Metrics/ClassLength: Exclude: - 'packages/forest_admin_agent/lib/forest_admin_agent/utils/schema/generator_field.rb' From 751d6eb3f7a36e3726a96c4075b0b00f6d087795 Mon Sep 17 00:00:00 2001 From: Matt Date: Wed, 4 Oct 2023 16:15:31 +0200 Subject: [PATCH 6/9] feat: handle serializer with example --- .rubocop.yml | 5 + .../routes/resources/list.rb | 49 +++++- .../serializer/forest_serializer.rb | 141 ++++++++++++++++++ .../forest_admin_agent/spec/spec_helper.rb | 1 + 4 files changed, 195 insertions(+), 1 deletion(-) create mode 100644 packages/forest_admin_agent/lib/forest_admin_agent/serializer/forest_serializer.rb diff --git a/.rubocop.yml b/.rubocop.yml index d137083fa..9b5f5f7c7 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -7,6 +7,7 @@ AllCops: NewCops: enable Exclude: - 'packages/forest_admin_datasource_active_record/spec/dummy/**/*' + - 'packages/forest_admin_agent/lib/forest_admin_agent/serializer/forest_serializer.rb' - 'node_modules/semantic-release-rubygem/**/*' Gemspec/OrderedDependencies: @@ -182,6 +183,10 @@ Metrics/ClassLength: Exclude: - 'packages/forest_admin_agent/lib/forest_admin_agent/utils/schema/generator_field.rb' +Style/OpenStructUse: + Exclude: + - 'packages/forest_admin_agent/lib/forest_admin_agent/routes/resources/**/*.rb' + RSpec/ExampleLength: CountAsOne: [ 'array', 'hash', 'method_call' ] Max: 20 diff --git a/packages/forest_admin_agent/lib/forest_admin_agent/routes/resources/list.rb b/packages/forest_admin_agent/lib/forest_admin_agent/routes/resources/list.rb index 008b66dcb..a9030d5ee 100644 --- a/packages/forest_admin_agent/lib/forest_admin_agent/routes/resources/list.rb +++ b/packages/forest_admin_agent/lib/forest_admin_agent/routes/resources/list.rb @@ -10,7 +10,54 @@ def setup_routes end def handle_request(args = {}) - { name: args['collection_name'], content: args['collection_name'] } + record = [ + { + 'id' => '1', + 'first_name' => 'aaa', + 'last_name' => 'bbb', + 'category' => { + 'id' => '1', + 'name' => 'category 1' + }, + 'orders' => [ + { + 'id' => '1', + 'name' => 'order 1' + }, + { + 'id' => '2', + 'name' => 'order 2' + } + ] + }, + { + 'id' => '2', + 'first_name' => 'toto', + 'last_name' => 'tata', + 'category' => { + 'id' => '1', + 'name' => 'category 1' + }, + 'orders' => [ + { + 'id' => '3', + 'name' => 'order 3' + }, + { + 'id' => '4', + 'name' => 'order 4' + } + ] + } + ] + + data = record.is_a?(Array) ? record.map { |record| OpenStruct.new(record) } : OpenStruct.new(record) + JSONAPI::Serializer.serialize( + data, + is_collection: data.is_a?(Array), + serializer: Serializer::ForestSerializer, + context: args['collection_name'] + ) end end end diff --git a/packages/forest_admin_agent/lib/forest_admin_agent/serializer/forest_serializer.rb b/packages/forest_admin_agent/lib/forest_admin_agent/serializer/forest_serializer.rb new file mode 100644 index 000000000..09e14542a --- /dev/null +++ b/packages/forest_admin_agent/lib/forest_admin_agent/serializer/forest_serializer.rb @@ -0,0 +1,141 @@ +require 'jsonapi-serializers' + +module ForestAdminAgent + module Serializer + class ForestSerializer + include JSONAPI::Serializer + + attr_accessor :attributes_map + attr_accessor :to_one_associations + attr_accessor :to_many_associations + + def base_url + ForestAdminRails.config[:prefix] + end + + def type + @options[:context] + end + + def format_name(attribute_name) + attribute_name.to_s + end + + def add_attribute(name, options = {}, &block) + @attributes_map ||= {} + @attributes_map[name] = format_field(name, options) + end + + def attributes + # forest_collection = ForestAdminAgent::Facades::Container.datasource.collection(@options[:context]) + # fields = forest_collection.getFields.reject { |field| field.is_a?(ForestAdminDatasourceToolkit::Schema::Relations::RelationSchema) } + fields = [:first_name, :last_name] + fields.each { |field| add_attribute(field.name) } + + return {} if attributes_map.nil? + attributes = {} + attributes_map.each do |attribute_name, attr_data| + next if !should_include_attr?(attribute_name, attr_data) + value = evaluate_attr_or_block(attribute_name, attr_data[:attr_or_block]) + attributes[format_name(attribute_name)] = value + end + attributes + end + + def add_to_one_association(name, options = {}, &block) + options[:include_links] = options.fetch(:include_links, true) + options[:include_data] = options.fetch(:include_data, false) + @to_one_associations ||= {} + @to_one_associations[name] = format_field(name, options) + end + + def add_to_many_association(name, options = {}, &block) + options[:include_links] = options.fetch(:include_links, true) + options[:include_data] = options.fetch(:include_data, false) + @to_many_associations ||= {} + @to_many_associations[name] = format_field(name, options) + end + + def has_relationships(type) + return {} if send("to_#{type}_associations").nil? + data = {} + send("to_#{type}_associations").each do |attribute_name, attr_data| + next if !should_include_attr?(attribute_name, attr_data) + data[attribute_name] = attr_data + end + data + end + + def format_field(name, options) + { + attr_or_block: block_given? ? block : name, + options: options, + } + end + + def relationships + # forest_collection = ForestAdminAgent::Facades::Container.datasource.collection(@options[:context]) + # relations_many_to_one = forest_collection.getFields.select { |field| field.is_a?(ForestAdminDatasourceToolkit::Schema::Relations::ManyToOneSchema) } + # relations_one_to_one = forest_collection.getFields.select { |field| field.is_a?(ForestAdminDatasourceToolkit::Schema::Relations::OneToOneSchema) } + + relations_one_to_one = [:category] + relations_one_to_one.each { |name| add_to_one_association(name) } + + data = {} + has_relationships('one').each do |attribute_name, attr_data| + formatted_attribute_name = format_name(attribute_name) + data[formatted_attribute_name] = {} + + if attr_data[:options][:include_links] + links_self = relationship_self_link(attribute_name) + links_related = relationship_related_link(attribute_name) + data[formatted_attribute_name]['links'] = {} if links_self || links_related + data[formatted_attribute_name]['links']['related'] = {} if links_self + data[formatted_attribute_name]['links']['related']['href'] = links_self if links_self + end + + object = has_one_relationship(attribute_name, attr_data) + if object.nil? + data[formatted_attribute_name]['data'] = nil + else + related_object_serializer = ForestSerializer.new(OpenStruct.new(object), @options.merge(context: attribute_name.to_s)) + data[formatted_attribute_name]['data'] = { + 'type' => related_object_serializer.type.to_s, + 'id' => related_object_serializer.id.to_s, + } + end + end + + relations_many_to_one = [:orders] + relations_many_to_one.each { |name| add_to_many_association(name) } + + has_relationships('many').each do |attribute_name, attr_data| + formatted_attribute_name = format_name(attribute_name) + + data[formatted_attribute_name] = {} + + if attr_data[:options][:include_links] + links_self = relationship_self_link(attribute_name) + links_related = relationship_related_link(attribute_name) + data[formatted_attribute_name]['links'] = {} if links_self || links_related + data[formatted_attribute_name]['links']['related'] = {} if links_self + data[formatted_attribute_name]['links']['related']['href'] = links_self if links_self + end + + if @_include_linkages.include?(formatted_attribute_name) || attr_data[:options][:include_data] + data[formatted_attribute_name]['data'] = [] + objects = has_many_relationship(attribute_name, attr_data) || [] + objects.each do |obj| + related_object_serializer = JSONAPI::Serializer.find_serializer(obj, @options) + data[formatted_attribute_name]['data'] << { + 'type' => related_object_serializer.type.to_s, + 'id' => related_object_serializer.id.to_s, + } + end + end + end + data + end + end + end +end diff --git a/packages/forest_admin_agent/spec/spec_helper.rb b/packages/forest_admin_agent/spec/spec_helper.rb index 4539ebced..c87246872 100644 --- a/packages/forest_admin_agent/spec/spec_helper.rb +++ b/packages/forest_admin_agent/spec/spec_helper.rb @@ -10,6 +10,7 @@ SimpleCov.start do add_filter 'spec' add_filter 'lib/forest_admin_agent/auth/oauth2/oidc_config.rb' + add_filter 'lib/forest_admin_agent/serializer/json_api_serializer.rb' end # Previous content of test helper now starts here From 74d56c24ad6e9d104123e64f11dd7f2423a35c2e Mon Sep 17 00:00:00 2001 From: Matt Date: Fri, 6 Oct 2023 11:22:05 +0200 Subject: [PATCH 7/9] feat(serializer): allow serializer to user model over array --- .../routes/resources/list.rb | 50 +------------------ .../serializer/forest_serializer.rb | 15 +++--- 2 files changed, 11 insertions(+), 54 deletions(-) diff --git a/packages/forest_admin_agent/lib/forest_admin_agent/routes/resources/list.rb b/packages/forest_admin_agent/lib/forest_admin_agent/routes/resources/list.rb index a9030d5ee..13875d6a8 100644 --- a/packages/forest_admin_agent/lib/forest_admin_agent/routes/resources/list.rb +++ b/packages/forest_admin_agent/lib/forest_admin_agent/routes/resources/list.rb @@ -10,54 +10,8 @@ def setup_routes end def handle_request(args = {}) - record = [ - { - 'id' => '1', - 'first_name' => 'aaa', - 'last_name' => 'bbb', - 'category' => { - 'id' => '1', - 'name' => 'category 1' - }, - 'orders' => [ - { - 'id' => '1', - 'name' => 'order 1' - }, - { - 'id' => '2', - 'name' => 'order 2' - } - ] - }, - { - 'id' => '2', - 'first_name' => 'toto', - 'last_name' => 'tata', - 'category' => { - 'id' => '1', - 'name' => 'category 1' - }, - 'orders' => [ - { - 'id' => '3', - 'name' => 'order 3' - }, - { - 'id' => '4', - 'name' => 'order 4' - } - ] - } - ] - - data = record.is_a?(Array) ? record.map { |record| OpenStruct.new(record) } : OpenStruct.new(record) - JSONAPI::Serializer.serialize( - data, - is_collection: data.is_a?(Array), - serializer: Serializer::ForestSerializer, - context: args['collection_name'] - ) + # is_collection true for a list false for a single record + # JSONAPI::Serializer.serialize(record, is_collection: true, serializer: Serializer::ForestSerializer) end end end diff --git a/packages/forest_admin_agent/lib/forest_admin_agent/serializer/forest_serializer.rb b/packages/forest_admin_agent/lib/forest_admin_agent/serializer/forest_serializer.rb index 09e14542a..405a68745 100644 --- a/packages/forest_admin_agent/lib/forest_admin_agent/serializer/forest_serializer.rb +++ b/packages/forest_admin_agent/lib/forest_admin_agent/serializer/forest_serializer.rb @@ -14,7 +14,8 @@ def base_url end def type - @options[:context] + class_name = object.class.name + @@class_names[class_name] ||= class_name.demodulize.underscore.freeze end def format_name(attribute_name) @@ -27,7 +28,7 @@ def add_attribute(name, options = {}, &block) end def attributes - # forest_collection = ForestAdminAgent::Facades::Container.datasource.collection(@options[:context]) + # forest_collection = ForestAdminAgent::Facades::Container.datasource.collection(object.class.name.demodulize.underscore) # fields = forest_collection.getFields.reject { |field| field.is_a?(ForestAdminDatasourceToolkit::Schema::Relations::RelationSchema) } fields = [:first_name, :last_name] fields.each { |field| add_attribute(field.name) } @@ -74,11 +75,12 @@ def format_field(name, options) end def relationships - # forest_collection = ForestAdminAgent::Facades::Container.datasource.collection(@options[:context]) + # forest_collection = ForestAdminAgent::Facades::Container.datasource.collection(object.class.name.demodulize.underscore) # relations_many_to_one = forest_collection.getFields.select { |field| field.is_a?(ForestAdminDatasourceToolkit::Schema::Relations::ManyToOneSchema) } # relations_one_to_one = forest_collection.getFields.select { |field| field.is_a?(ForestAdminDatasourceToolkit::Schema::Relations::OneToOneSchema) } - relations_one_to_one = [:category] + # TO REMOVE + relations_one_to_one = [] relations_one_to_one.each { |name| add_to_one_association(name) } data = {} @@ -98,7 +100,7 @@ def relationships if object.nil? data[formatted_attribute_name]['data'] = nil else - related_object_serializer = ForestSerializer.new(OpenStruct.new(object), @options.merge(context: attribute_name.to_s)) + related_object_serializer = ForestSerializer.new(object, @options) data[formatted_attribute_name]['data'] = { 'type' => related_object_serializer.type.to_s, 'id' => related_object_serializer.id.to_s, @@ -106,7 +108,8 @@ def relationships end end - relations_many_to_one = [:orders] + # TO REMOVE + relations_many_to_one = [] relations_many_to_one.each { |name| add_to_many_association(name) } has_relationships('many').each do |attribute_name, attr_data| From af869861148513b35bb60bf1eed01312e78da56b Mon Sep 17 00:00:00 2001 From: Matt Date: Fri, 6 Oct 2023 16:28:41 +0200 Subject: [PATCH 8/9] fix: serializer base-url --- .../lib/forest_admin_agent/serializer/forest_serializer.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/forest_admin_agent/lib/forest_admin_agent/serializer/forest_serializer.rb b/packages/forest_admin_agent/lib/forest_admin_agent/serializer/forest_serializer.rb index 405a68745..e153d74e7 100644 --- a/packages/forest_admin_agent/lib/forest_admin_agent/serializer/forest_serializer.rb +++ b/packages/forest_admin_agent/lib/forest_admin_agent/serializer/forest_serializer.rb @@ -10,7 +10,7 @@ class ForestSerializer attr_accessor :to_many_associations def base_url - ForestAdminRails.config[:prefix] + Facades::Container.cache(:prefix) end def type From 656150680b8bcadd0db41275dd1701927e1e0467 Mon Sep 17 00:00:00 2001 From: Matt Date: Fri, 6 Oct 2023 16:30:15 +0200 Subject: [PATCH 9/9] chore: comment --- .../lib/forest_admin_agent/serializer/forest_serializer.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/forest_admin_agent/lib/forest_admin_agent/serializer/forest_serializer.rb b/packages/forest_admin_agent/lib/forest_admin_agent/serializer/forest_serializer.rb index e153d74e7..e5ace13f3 100644 --- a/packages/forest_admin_agent/lib/forest_admin_agent/serializer/forest_serializer.rb +++ b/packages/forest_admin_agent/lib/forest_admin_agent/serializer/forest_serializer.rb @@ -30,7 +30,8 @@ def add_attribute(name, options = {}, &block) def attributes # forest_collection = ForestAdminAgent::Facades::Container.datasource.collection(object.class.name.demodulize.underscore) # fields = forest_collection.getFields.reject { |field| field.is_a?(ForestAdminDatasourceToolkit::Schema::Relations::RelationSchema) } - fields = [:first_name, :last_name] + # TO REMOVE + fields = [] fields.each { |field| add_attribute(field.name) } return {} if attributes_map.nil?