lib/cloud_controller/rest_controller/secure_eager_loader.rb
module VCAP::CloudController::RestController
class SecureEagerLoader
def eager_load_dataset(dataset, starting_controller_class, default_visibility_filter, additional_visibility_filters, depth)
eager_load_hash = build_eager_load_hash(
starting_controller_class,
dataset.model,
default_visibility_filter,
additional_visibility_filters,
depth
)
dataset.eager(eager_load_hash)
end
private
def build_eager_load_hash(controller_class, model_class, default_visibility_filter, additional_visibility_filters, depth)
associated_controller = controller_class
# model_class cannot just be inferred from controller_class.model
# because ServiceInstancesController can be used to present objects
# from ManagedServiceInstance and ServiceInstance datasets.
# Ideally that will not happen.
associated_controller ||= VCAP::CloudController.controller_from_model_name(model_class.name)
all_relationships = {}
[associated_controller.to_one_relationships,
associated_controller.to_many_relationships].each do |rel|
all_relationships.merge!(rel) if rel&.any?
end
eager_load_hash = {}
all_relationships.each do |relationship_name, association|
association_name = association.association_name
association_model_class = model_class.association_reflection(association_name)
unless association_model_class
# Since we are using STI in some models (e.g. Domain, ServiceInstance)
# we are not able to find association on the parent class defined on a child class.
# We are assuming that parent will have an association with a suffix.
association_name = :"#{association_name}_sti_eager_load"
association_model_class = model_class.association_reflection(association_name)
end
unless association_model_class
raise ArgumentError.new(
"Cannot resolve association #{association_name} on #{model_class} " \
'while trying to build eager loading hash'
)
end
visibility_filter = default_visibility_filter
additional_filter = additional_visibility_filters[relationship_name]
visibility_filter = proc { |ds| additional_filter.call(default_visibility_filter.call(ds)) } if additional_filter
unless association.link_only?
if depth > 0
eager_load_hash[association_name] = {
visibility_filter => build_eager_load_hash(
nil,
association_model_class.associated_class,
default_visibility_filter,
additional_visibility_filters,
depth - 1
)
}
elsif association.is_a?(ControllerDSL::ToOneAttribute)
# Preload one-to-one since we need to know record's guid
eager_load_hash[association_name] = visibility_filter
end
end
end
eager_load_hash
end
end
end