Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
9bf5993
feat: send apimap to forest
nicolasalexandre9 Oct 2, 2023
eb4b1bb
test: update unit tests
nicolasalexandre9 Oct 2, 2023
d751aea
test: add test on agent factory
nicolasalexandre9 Oct 3, 2023
7514abd
feat: add parse caller
nicolasalexandre9 Oct 4, 2023
cf10077
feat: implement list method with filter and projection
nicolasalexandre9 Oct 6, 2023
40509e6
feat: return serialised response for list
nicolasalexandre9 Oct 9, 2023
b2567d4
chore: lint
nicolasalexandre9 Oct 9, 2023
3d5fc6f
feat: add route count
nicolasalexandre9 Oct 10, 2023
8c1719a
chore: lint
nicolasalexandre9 Oct 10, 2023
4758a3e
chore: update rubocop config
nicolasalexandre9 Oct 10, 2023
91a54bc
test: add tests on schema utils
nicolasalexandre9 Oct 12, 2023
9eaedc2
test: add tests on projection class
nicolasalexandre9 Oct 12, 2023
a8db469
test: add tests on aggregation class
nicolasalexandre9 Oct 12, 2023
8ce1459
test: add tests on query string parser
nicolasalexandre9 Oct 12, 2023
d6131e1
chore: lint
nicolasalexandre9 Oct 12, 2023
4b25561
test: add tests on list route
nicolasalexandre9 Oct 12, 2023
da90e51
refactor: test forest_provider
matthv Oct 12, 2023
cdc59c7
chore: add test on forest_admin_api_requester
matthv Oct 12, 2023
ad33263
test: update test on list route
nicolasalexandre9 Oct 12, 2023
6e1fe5f
test: add tests on count route
nicolasalexandre9 Oct 12, 2023
50e7547
fix: tests after rebase
nicolasalexandre9 Oct 13, 2023
05ce6a7
chore: update
matthv Oct 13, 2023
4ff53cc
chore: update
matthv Oct 13, 2023
d34dc87
test: remove puts
nicolasalexandre9 Oct 13, 2023
aad010b
fix(test): fix instantiation of user object
nicolasalexandre9 Oct 13, 2023
b4304ed
chore: lint
nicolasalexandre9 Oct 13, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ AllCops:
Exclude:
- 'packages/forest_admin_datasource_active_record/spec/dummy/**/*'
- 'packages/forest_admin_agent/lib/forest_admin_agent/serializer/forest_serializer.rb'
- 'packages/forest_admin_agent/lib/forest_admin_agent/serializer/forest_serializer_override.rb'
- 'node_modules/semantic-release-rubygem/**/*'

Gemspec/OrderedDependencies:
Expand Down Expand Up @@ -37,11 +38,16 @@ 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_agent/lib/forest_admin_agent/builder/agent_factory.rb'
- 'packages/forest_admin_agent/lib/forest_admin_agent/utils/query_string_parser.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'
- 'packages/forest_admin_datasource_toolkit/lib/forest_admin_datasource_toolkit/components/query/projection.rb'
- 'packages/forest_admin_datasource_toolkit/lib/forest_admin_datasource_toolkit/components/query/projection_factory.rb'
-

Metrics/CyclomaticComplexity:
Exclude:
Expand Down Expand Up @@ -159,6 +165,8 @@ Metrics/ParameterLists:
Exclude:
- 'packages/forest_admin_datasource_toolkit/lib/forest_admin_datasource_toolkit/schema/relations/many_to_many_schema.rb'
- 'packages/forest_admin_datasource_toolkit/lib/forest_admin_datasource_toolkit/schema/column_schema.rb'
- 'packages/forest_admin_datasource_toolkit/lib/forest_admin_datasource_toolkit/components/caller.rb'
- 'packages/forest_admin_datasource_toolkit/lib/forest_admin_datasource_toolkit/components/query/filter.rb'

Metrics/ModuleLength:
CountAsOne: [ 'array', 'hash', 'method_call' ]
Expand Down Expand Up @@ -197,6 +205,8 @@ RSpec/MultipleExpectations:

Layout/LineLength:
Max: 120
Exclude:
- 'packages/forest_admin_agent/spec/shared/caller.rb'

RSpec/MultipleMemoizedHelpers:
Max: 10
2 changes: 2 additions & 0 deletions packages/forest_admin_agent/forest_admin_agent.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,6 @@ admin work on any Ruby application."
spec.add_dependency "rake", "~> 13.0"
spec.add_dependency "rack-cors", "~> 2.0"
spec.add_dependency "zeitwerk", "~> 2.3"
spec.add_dependency "faraday", "~> 2.7"
spec.add_dependency "activesupport", "~> 7.0"
end
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,44 @@ class AgentFactory
def setup(options)
@options = options
@has_env_secret = options.to_h.key?(:env_secret)
# @customizer = DatasourceCustomizer.new
@customizer = ForestAdminDatasourceToolkit::Datasource.new
build_container
build_cache
build_logger
end

def add_datasource(datasource)
datasource.collections.each { |_name, collection| @customizer.add_collection(collection) }
self
end

def build
# @customizer.datasource
@container.register(:datasource, {})
@container.register(:datasource, @customizer)
send_schema
end

private

def send_schema(force: false)
# todo
return unless @has_env_secret

schema = ForestAdminAgent::Utils::Schema::SchemaEmitter.get_serialized_schema(@customizer)
schema_is_know = @container.key?(:schema_file_hash) &&
@container.resolve(:schema_file_hash).get('value') == schema[:meta][:schemaFileHash]

if !schema_is_know || force
# Logger::log('Info', 'schema was updated, sending new version');
client = ForestAdminAgent::Http::ForestAdminApiRequester.new
client.post('/forest/apimaps', schema)
schema_file_hash_cache = Lightly.new(life: TTL_SCHEMA, dir: @options[:cache_dir].to_s)
schema_file_hash_cache.get 'value' do
schema[:meta][:schemaFileHash]
end
@container.register(:schema_file_hash, schema_file_hash_cache)
else
@container.resolve(:logger)
# TODO: Logger::log('Info', 'Schema was not updated since last run');
end
end

def build_container
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ def self.instance
ForestAdminAgent::Builder::AgentFactory.instance.container
end

def self.datasource
instance.resolve(:datasource)
end

def self.config_from_cache
instance.resolve(:cache).get('config')
end
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
require 'faraday'

module ForestAdminAgent
module Http
class ForestAdminApiRequester
def initialize
@headers = {
'Content-Type' => 'application/json',
'forest-secret-key' => Facades::Container.cache(:env_secret)
}
@client = Faraday.new(
Facades::Container.cache(:forest_server_url),
{
headers: @headers
}
)
end

def get(url, params)
@client.get(url, params.to_json)
end

def post(url, params)
@client.post(url, params.to_json)
end
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ def self.routes
# api_charts_routes,
System::HealthCheck.new.routes,
Security::Authentication.new.routes,
Resources::List.new.routes
Resources::List.new.routes,
Resources::Count.new.routes
].inject(&:merge)
end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ module ForestAdminAgent
module Routes
class AbstractAuthenticatedRoute < AbstractRoute
def build(args = {})
# TODO: handle call permissions
Facades::Whitelist.check_ip(args[:headers]['action_dispatch.remote_ip'].to_s)
if args.dig(:headers, 'action_dispatch.remote_ip')
Facades::Whitelist.check_ip(args[:headers]['action_dispatch.remote_ip'].to_s)
end
super
end
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ def initialize
setup_routes
end

def build(args)
@datasource = ForestAdminAgent::Facades::Container.datasource
@collection = @datasource.collection(args[:params]['collection_name'])
end

def routes
@routes ||= {}
end
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
require 'jsonapi-serializers'

module ForestAdminAgent
module Routes
module Resources
class Count < AbstractRoute
include ForestAdminAgent::Builder
def setup_routes
add_route('forest_count', 'get', '/:collection_name/count', ->(args) { handle_request(args) })

self
end

def handle_request(args = {})
build(args)
caller = ForestAdminAgent::Utils::QueryStringParser.parse_caller(args)
filter = ForestAdminDatasourceToolkit::Components::Query::Filter.new
aggregation = ForestAdminDatasourceToolkit::Components::Query::Aggregation.new(operation: 'Count')
result = @collection.aggregate(caller, filter, aggregation)

{
name: args[:params]['collection_name'],
content: {
count: result[0][:value]
}
}
end
end
end
end
end
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
require 'jsonapi-serializers'

module ForestAdminAgent
module Routes
module Resources
Expand All @@ -10,10 +12,23 @@ def setup_routes
end

def handle_request(args = {})
# is_collection true for a list false for a single record
# JSONAPI::Serializer.serialize(record, is_collection: true, serializer: Serializer::ForestSerializer)
build(args)
{ name: args['collection_name'], content: args['collection_name'] }
caller = ForestAdminAgent::Utils::QueryStringParser.parse_caller(args)
filter = ForestAdminDatasourceToolkit::Components::Query::Filter.new(
page: ForestAdminAgent::Utils::QueryStringParser.parse_pagination(args)
)
projection = ForestAdminAgent::Utils::QueryStringParser.parse_projection_with_pks(@collection, args)
records = @collection.list(caller, filter, projection)

{
name: args[:params]['collection_name'],
content: JSONAPI::Serializer.serialize(
records,
is_collection: true,
serializer: Serializer::ForestSerializer,
include: projection.relations.keys
)
}
end
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,10 @@ def setup_routes
end

def handle_authentication(args = {})
# Facades::Whitelist.check_ip(args[:headers]['action_dispatch.remote_ip'].to_s)
rendering_id = get_and_check_rendering_id args
if args.dig(:headers, 'action_dispatch.remote_ip')
Facades::Whitelist.check_ip(args[:headers]['action_dispatch.remote_ip'].to_s)
end
rendering_id = get_and_check_rendering_id args[:params]

{
content: {
Expand All @@ -37,8 +39,10 @@ def handle_authentication(args = {})
end

def handle_authentication_callback(args = {})
# Facades::Whitelist.check_ip(args[:headers]['action_dispatch.remote_ip'].to_s)
token = auth.verify_code_and_generate_token(args)
if args.dig(:headers, 'action_dispatch.remote_ip')
Facades::Whitelist.check_ip(args[:headers]['action_dispatch.remote_ip'].to_s)
end
token = auth.verify_code_and_generate_token(args[:params])
token_data = JWT.decode(
token,
Facades::Container.cache(:auth_secret),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ class ForestSerializer
attr_accessor :to_one_associations
attr_accessor :to_many_associations

JSONAPI::Serializer.send(:include, ForestSerializerOverride)

def initialize(object, options = nil)
super
end

def base_url
Facades::Container.cache(:prefix)
end
Expand All @@ -27,12 +33,17 @@ def add_attribute(name, options = {}, &block)
@attributes_map[name] = format_field(name, options)
end

def format_field(name, options)
{
attr_or_block: block_given? ? block : name,
options: 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) }
forest_collection = ForestAdminAgent::Facades::Container.datasource.collection(object.class.name.demodulize.underscore)
fields = forest_collection.fields.select { |_field_name, field| field.type == 'Column' }
fields.each { |field_name, _field| add_attribute(field_name) }

return {} if attributes_map.nil?
attributes = {}
Expand All @@ -51,41 +62,42 @@ def add_to_one_association(name, options = {}, &block)
@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)
def has_one_relationships
return {} if @to_one_associations.nil?
data = {}
@to_one_associations.each do |attribute_name, attr_data|
next if !should_include_attr?(attribute_name, attr_data)
data[attribute_name.to_sym] = attr_data
end
data
end

def has_relationships(type)
return {} if send("to_#{type}_associations").nil?
def has_many_relationships
return {} if @to_many_associations.nil?
data = {}
send("to_#{type}_associations").each do |attribute_name, attr_data|
@to_many_associations.each do |attribute_name, attr_data|
next if !should_include_attr?(attribute_name, attr_data)
data[attribute_name] = attr_data
data[attribute_name.to_sym] = attr_data
end
data
end

def format_field(name, options)
{
attr_or_block: block_given? ? block : name,
options: options,
}
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 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) }
forest_collection = ForestAdminAgent::Facades::Container.datasource.collection(object.class.name.demodulize.underscore)
relations_to_many = forest_collection.fields.select { |_field_name, field| field.type == 'HasMany' || field.type == 'ManyToMany' }
relations_to_one = forest_collection.fields.select { |_field_name, field| field.type == 'OneToOne' || field.type == 'ManyToOne' }

# TO REMOVE
relations_one_to_one = []
relations_one_to_one.each { |name| add_to_one_association(name) }
relations_to_one.each { |field_name, _field| add_to_one_association(field_name) }

data = {}
has_relationships('one').each do |attribute_name, attr_data|
has_one_relationships.each do |attribute_name, attr_data|
formatted_attribute_name = format_name(attribute_name)
data[formatted_attribute_name] = {}

Expand All @@ -109,11 +121,9 @@ def relationships
end
end

# TO REMOVE
relations_many_to_one = []
relations_many_to_one.each { |name| add_to_many_association(name) }
relations_to_many.each { |field_name, _field| add_to_many_association(field_name) }

has_relationships('many').each do |attribute_name, attr_data|
has_many_relationships.each do |attribute_name, attr_data|
formatted_attribute_name = format_name(attribute_name)

data[formatted_attribute_name] = {}
Expand Down
Loading