Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,14 @@ def routes
end

def add_route(name, method, uri, closure, format = 'json')
@routes[name] = { method: method, uri: uri, closure: closure, format: format }
instrumented = lambda do |args|
ForestAdminDatasourceToolkit::Monitoring.instrument(
'request',
{ route: name, collection: args.dig(:params, 'collection_name'),
id: args.dig(:params, 'id'), method: method }
) { closure.call(args) }
end
@routes[name] = { method: method, uri: uri, closure: instrumented, format: format }
end

def setup_routes
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
require 'spec_helper'
require 'active_support/isolated_execution_state'
require 'active_support/notifications'

module ForestAdminAgent
module Routes
describe AbstractRoute do
let(:route_class) do
Class.new(described_class) do
def setup_routes
add_route('forest_test', 'get', '/test', ->(args) { { echoed: args.dig(:params, 'collection_name') } })
self
end
end
end

it 'wraps the closure in a request.forest_admin notification and passes the return value through' do
events = []
subscriber = ::ActiveSupport::Notifications.subscribe('request.forest_admin') do |*args|
events << ::ActiveSupport::Notifications::Event.new(*args)
end

route = route_class.new.routes['forest_test']
result = route[:closure].call({ params: { 'collection_name' => 'user', 'id' => '42' } })

::ActiveSupport::Notifications.unsubscribe(subscriber)

expect(result).to eq({ echoed: 'user' })
expect(events.size).to eq(1)
expect(events.first.payload).to eq({ route: 'forest_test', collection: 'user', id: '42', method: 'get' })
expect(events.first.duration).to be >= 0
end
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ def initialize(collection, caller)
@caller = caller
end

def name
@collection.name
end

def native_driver(&block)
@collection.native_driver(&block)
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ def execute(caller, name, data, filter = nil)
context = get_context(caller, action, data, filter)

result_builder = ResultBuilder.new
result = action.execute.call(context, result_builder)
result = ForestAdminDatasourceToolkit::Monitoring.instrument(
'action', { collection: self.name, action: name }, caller: caller
) { action.execute.call(context, result_builder) }

return result if result.is_a? Hash

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ def render_chart(caller, name, record_id, parameters = {})
context = ChartContext.new(self, caller, record_id, parameters)
result_builder = ResultBuilder.new

return @charts[name].call(context, result_builder)
return ForestAdminDatasourceToolkit::Monitoring.instrument(
'chart', { collection: self.name, chart: name }, caller: caller
) { @charts[name].call(context, result_builder) }
end

@child_collection.render_chart(caller, name, record_id, parameters)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,12 @@ def render_chart(caller, name, parameters = {})
chart_definition = @charts[name]

if chart_definition
return chart_definition.call(
DatasourceChartContext.new(self, caller, parameters),
ResultBuilder.new
)
return ForestAdminDatasourceToolkit::Monitoring.instrument('chart', { chart: name }, caller: caller) do
chart_definition.call(
DatasourceChartContext.new(self, caller, parameters),
ResultBuilder.new
)
end
end

super
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ def self.queue_field(ctx, collection, new_path, paths, flatten)

paths.push(new_path)

flatten << compute_field(ctx, computed, computed_dependencies, dependency_values)
flatten << ForestAdminDatasourceToolkit::Monitoring.instrument(
'computed_field', { collection: collection.name, field: new_path }, caller: ctx.caller
) { compute_field(ctx, computed, computed_dependencies, dependency_values) }
end

def self.compute_from_records(ctx, collection, records_projection, desired_projection, records)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ class HookCollectionDecorator < ForestAdminDatasourceToolkit::Decorators::Collec
def initialize(child_collection, datasource)
super
@hooks = {
'List' => Hooks.new,
'Create' => Hooks.new,
'Update' => Hooks.new,
'Delete' => Hooks.new,
'Aggregate' => Hooks.new
'List' => Hooks.new('List'),
'Create' => Hooks.new('Create'),
'Update' => Hooks.new('Update'),
'Delete' => Hooks.new('Delete'),
'Aggregate' => Hooks.new('Aggregate')
}
end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,35 @@ module Hook
class Hooks
attr_reader :before, :after

def initialize
def initialize(type = nil)
@type = type
@before = []
@after = []
end

def execute_before(context)
@before.each { |hook| hook.call(context) }
instrument('before', context) { @before.each { |hook| hook.call(context) } }
end

def execute_after(context)
@after.each { |hook| hook.call(context) }
instrument('after', context) { @after.each { |hook| hook.call(context) } }
end

def add_handler(position, hook)
position == 'After' ? @after << hook : @before << hook
end

private

def instrument(position, context, &block)
hooks = position == 'before' ? @before : @after
return yield if hooks.empty?

ForestAdminDatasourceToolkit::Monitoring.instrument(
'hook', { collection: context.collection.name, operation: @type, position: position },
caller: context.caller, &block
)
end
end
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,9 @@ def compute_equivalent(caller, leaf, replacements)
raise Exceptions::ForestException, "Operator replacement cycle: #{sub_replacements.join(" -> ")}"
end

result = handler.call(leaf.value, Context::CollectionCustomizationContext.new(self, caller))
result = ForestAdminDatasourceToolkit::Monitoring.instrument(
'operator_emulation', { collection: name, field: leaf.field, operator: leaf.operator }, caller: caller
) { handler.call(leaf.value, Context::CollectionCustomizationContext.new(self, caller)) }

if result
equivalent = result.class < Nodes::ConditionTree ? result : ConditionTreeFactory.from_plain_object(result)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ class OverrideCollectionDecorator < ForestAdminDatasourceToolkit::Decorators::Co
def create(caller, data)
if @create_handler
context = CreateOverrideCustomizationContext.new(@child_collection, caller, data)
return @create_handler.call(context)
return ForestAdminDatasourceToolkit::Monitoring.instrument(
'override', { collection: name, operation: 'create' }, caller: caller
) { @create_handler.call(context) }
end

super
Expand All @@ -22,7 +24,9 @@ def add_create_handler(handler)
def update(caller, filter, patch)
if @update_handler
context = UpdateOverrideCustomizationContext.new(@child_collection, caller, filter, patch)
return @update_handler.call(context)
return ForestAdminDatasourceToolkit::Monitoring.instrument(
'override', { collection: name, operation: 'update' }, caller: caller
) { @update_handler.call(context) }
end

super
Expand All @@ -35,7 +39,9 @@ def add_update_handler(handler)
def delete(caller, filter)
if @delete_handler
context = DeleteOverrideCustomizationContext.new(@child_collection, caller, filter)
return @delete_handler.call(context)
return ForestAdminDatasourceToolkit::Monitoring.instrument(
'override', { collection: name, operation: 'delete' }, caller: caller
) { @delete_handler.call(context) }
end

super
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,11 @@ def refine_filter(caller, filter)
tree = default_replacer(filter.search, filter.search_extended)

if @replacer
plain_tree = @replacer.call(filter.search, filter.search_extended, ctx)
plain_tree = ForestAdminDatasourceToolkit::Monitoring.instrument(
'search', { collection: name }, caller: caller
) do
@replacer.call(filter.search, filter.search_extended, ctx)
end
tree = ConditionTreeFactory.from_plain_object(plain_tree)
end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,24 +25,26 @@ def refine_schema(sub_schema)
sub_schema
end

def refine_filter(_caller, filter = nil)
def refine_filter(caller, filter = nil)
return nil unless filter

condition_tree = filter.condition_tree
segment = filter.segment
if segment && @segments.key?(segment)
condition_tree = compute_segment(segment, filter)
condition_tree = compute_segment(segment, filter, caller)
segment = nil
end

filter.override(condition_tree: condition_tree, segment: segment)
end

def compute_segment(segment_name, filter)
def compute_segment(segment_name, filter, caller)
definition = @segments[segment_name]

result = if definition.respond_to? :call
definition.call(Context::CollectionCustomizationContext.new(self, caller))
ForestAdminDatasourceToolkit::Monitoring.instrument(
'segment', { collection: name, segment: segment_name }, caller: caller
) { definition.call(Context::CollectionCustomizationContext.new(self, caller)) }
else
definition
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,20 @@ def rewrite_key(context, key, used)

if field_schema&.type == 'Column'
# We either call the customer handler or a default one that does nothing.
handler = @handlers[key] || proc { |v| { key => v } }
field_patch = if context.record.key?(key) && handler.call(context.record[key], context)
handler.call(context.record[key], context)
else
{}
end
user_handler = @handlers[key]
handler = user_handler || proc { |v| { key => v } }
# ponytail: single evaluation (the previous code called the handler twice); only the
# user handler is instrumented — the default no-op proc isn't user code.
raw = if context.record.key?(key)
if user_handler
ForestAdminDatasourceToolkit::Monitoring.instrument(
'write', { collection: name, field: key }, caller: context.caller
) { handler.call(context.record[key], context) }
else
handler.call(context.record[key], context)
end
end
field_patch = raw || {}

if field_patch && !field_patch.is_a?(Hash)
raise ForestException, "The write handler of #{key} should return an Hash or nothing."
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ module ForestAdminDatasourceCustomizer

describe DatasourceCustomizer do
let(:datasource) { ForestAdminDatasourceToolkit::Datasource.new }
let(:caller) do
instance_double(ForestAdminDatasourceToolkit::Components::Caller, id: 1, rendering_id: 1, project: 'test')
end

before do
@collection = instance_double(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,17 @@ module Action
expect(result).to eq({ response_headers: {}, type: 'Success', message: 'Success', invalidated: [], html: nil })
end

it 'emits an action.forest_admin notification' do
events = []
sub = ::ActiveSupport::Notifications.subscribe('action.forest_admin') do |*a|
events << ::ActiveSupport::Notifications::Event.new(*a)
end
@decorated_book.execute(caller, 'make photocopy', {}, Filter.new)
::ActiveSupport::Notifications.unsubscribe(sub)

expect(events.map(&:payload)).to include(include(action: 'make photocopy'))
end

it 'generate empty form' do
form = @decorated_book.get_form(caller, 'make photocopy', {}, Filter.new)
expect(form).to eq([])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,17 @@ module Chart

expect(result).to eq({ countCurrent: 2 })
end

it 'emits a chart.forest_admin notification' do
events = []
sub = ::ActiveSupport::Notifications.subscribe('chart.forest_admin') do |*a|
events << ::ActiveSupport::Notifications::Event.new(*a)
end
@decorated_book.render_chart(caller, 'new_chart', [123])
::ActiveSupport::Notifications.unsubscribe(sub)

expect(events.map(&:payload)).to include(include(chart: 'new_chart'))
end
end
end
end
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
require 'spec_helper'
require 'active_support/notifications'

module ForestAdminDatasourceCustomizer
module Decorators
Expand Down Expand Up @@ -193,6 +194,21 @@ module Computed
])
end

it 'list() should emit a computed_field.forest_admin notification' do
events = []
subscriber = ::ActiveSupport::Notifications.subscribe('computed_field.forest_admin') do |*args|
events << ::ActiveSupport::Notifications::Event.new(*args)
end

@new_books.list(caller, Filter.new, Projection.new(['title', 'author:fullName']))

::ActiveSupport::Notifications.unsubscribe(subscriber)

expect(events.map(&:payload)).to include(
include(collection: 'book', field: 'author:fullName', user_id: 1, rendering_id: 1, project: 'terminator')
)
end

it 'aggregate() should use the child implementation when relevant' do
rows = @new_books.aggregate(
caller,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ module Hook
describe HookCollectionDecorator do
subject(:hook_collection_decorator) { described_class }

let(:caller) { instance_double(ForestAdminDatasourceToolkit::Components::Caller) }
let(:caller) do
instance_double(ForestAdminDatasourceToolkit::Components::Caller, id: 1, rendering_id: 1, project: 'test')
end
let(:aggregation) { instance_double(ForestAdminDatasourceToolkit::Components::Query::Aggregation) }

before do
Expand Down Expand Up @@ -60,6 +62,19 @@ module Hook

expect(spy).to have_received(:call).once
end

it 'emits a hook.forest_admin notification for registered hooks' do
spy = instance_double(Proc, call: nil)
@decorated_transaction.add_hook('Before', 'Create', spy)
events = []
sub = ::ActiveSupport::Notifications.subscribe('hook.forest_admin') do |*a|
events << ::ActiveSupport::Notifications::Event.new(*a)
end
@decorated_transaction.create(caller, [])
::ActiveSupport::Notifications.unsubscribe(sub)

expect(events.map(&:payload)).to include(include(operation: 'Create', position: 'before', collection: 'transaction'))
end
end

describe 'on a update' do
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ module Override
describe OverrideCollectionDecorator do
subject(:override_collection_decorator) { described_class }

let(:caller) { instance_double(ForestAdminDatasourceToolkit::Components::Caller) }
let(:caller) { instance_double(ForestAdminDatasourceToolkit::Components::Caller, id: 1, rendering_id: 1, project: 'test') }

before do
datasource = Datasource.new
Expand Down
Loading
Loading