diff --git a/.rubocop.yml b/.rubocop.yml index 8599907af..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: @@ -36,14 +37,21 @@ 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' + - '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' 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: @@ -164,19 +172,21 @@ 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' +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/.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 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" 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..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,7 +10,8 @@ def setup_routes end def handle_request(args = {}) - { name: args['collection_name'], content: 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 new file mode 100644 index 000000000..e5ace13f3 --- /dev/null +++ b/packages/forest_admin_agent/lib/forest_admin_agent/serializer/forest_serializer.rb @@ -0,0 +1,145 @@ +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 + Facades::Container.cache(:prefix) + end + + def type + class_name = object.class.name + @@class_names[class_name] ||= class_name.demodulize.underscore.freeze + 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(object.class.name.demodulize.underscore) + # fields = forest_collection.getFields.reject { |field| field.is_a?(ForestAdminDatasourceToolkit::Schema::Relations::RelationSchema) } + # TO REMOVE + fields = [] + 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(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) } + + # TO REMOVE + relations_one_to_one = [] + 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(object, @options) + data[formatted_attribute_name]['data'] = { + 'type' => related_object_serializer.type.to_s, + 'id' => related_object_serializer.id.to_s, + } + end + end + + # 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| + 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/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) 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