lib/contrast/framework/rails/support.rb in contrast-agent-4.9.1 vs lib/contrast/framework/rails/support.rb in contrast-agent-4.10.0
- old
+ new
@@ -11,10 +11,12 @@
module Rails
# Used when Rails is present to define framework specific behavior
class Support
extend Contrast::Framework::BaseSupport
extend Contrast::Framework::Rails::Patch::Support
+ include Contrast::Components::Logger::InstanceMethods
+ extend Contrast::Components::Logger::InstanceMethods
class << self
RAILS_MODULE_NAME_VERSION = Gem::Version.new('6.0.0')
def detection_class
@@ -47,35 +49,40 @@
end
# Find the current route, based on the provided Request wrapper
#
# @param request[Contrast::Agent::Request]
- # @return [Contrast::Api::Dtm::RouteCoverage]
+ # @return [Contrast::Api::Dtm::RouteCoverage, nil]
def current_route request
return unless ::Rails.cs__respond_to?(:application)
+ # ActionDispatch::Journey::Path::Pattern::MatchData, Hash, ActionDispatch::Journey::Route, Array<String>
match, _params, route, path = get_full_route(request.rack_request)
+ unless route
+ logger.warn('Unable to determine the current route of this request')
+ return
+ end
original_url = request.rack_request.path_info
-
+ mounted_app = route&.app&.app
# Route is either the final rails route, or a router that points to a Sinatra controller.
- if Contrast::Framework::Sinatra::Support.sinatra_controller?(route.app.app)
- # Create a request copied from current request, but with the base path removed from path_info.
- new_req = ::ActionDispatch::Request.new(request.env)
- new_req.path_info = new_req.path_info.gsub((path << match).join, '')
-
- return Contrast::Framework::Sinatra::Support.current_route(new_req, route.app.app, original_url)
+ if mounted_app && Contrast::Framework::Sinatra::Support.sinatra_controller?(mounted_app)
+ return mounted_sinatra_route(request, match, path, route, original_url)
end
+ if mounted_app && Contrast::Framework::Grape::Support.grape_controller?(mounted_app)
+ return mounted_grape_route(request, match, path, route, original_url)
+ end
Contrast::Api::Dtm::RouteCoverage.from_action_dispatch_journey(route, original_url)
- rescue StandardError => _e
+ rescue StandardError => e
+ logger.warn('Unable to determine the current route of this request', e)
nil
end
# Copy a request for modification.
#
- # @param [::ActionDispatch::Request] original env.
+ # @param env [::ActionDispatch::Request] original env.
# @return [::ActionDispatch::Request] a copy of original env with rails env merged.
def retrieve_request env
rails_env = ::Rails.application.env_config.merge(env)
::ActionDispatch::Request.new(rails_env || env)
end
@@ -89,22 +96,25 @@
private
# Determine if route is a Rails engine route.
#
- # @param [Object] app or route that points to a ::Rails::Engine
+ # @param route [Object] app or route that points to a ::Rails::Engine
# @return [bool] whether the router is an engine or not.
def engine_route? route
+ return false unless route&.app&.app
+
route.app.is_a?(::ActionDispatch::Routing::Mapper::Constraints) && route.app.app < ::Rails::Engine
end
# Recursively get final route traversing engines as required.
#
# @param request [::Rack::Request] the rack request as will be handed to rails controller.
- # @param top_router [::ActionDispatch::Journer::Router] the current router relative to the previous.
+ # @param top_router [::ActionDispatch::Journey::Router] the current router relative to the previous.
# @param path [Array<String>] the chunks of path that have been seen.
- # @return [Array<array>] the final set of rails route classes.
+ # @return [Array<Object>] the final set of rails route classes.
+ # ActionDispatch::Journey::Path::Pattern::MatchData, Hash, ActionDispatch::Journey::Route, Array<String>
def get_full_route request, top_router = ::Rails.application.routes.router, path = []
return if (route_matches = top_router.send(:find_routes, request)).empty?
match, params, route = route_matches.first
@@ -129,9 +139,46 @@
elsif route.app.app.cs__respond_to?(:routes)
route_list += find_all_routes(route.app.app, [])
end
end
route_list
+ end
+
+ # @param request[Contrast::Agent::Request]
+ # @param match [ActionDispatch::Journey::Path::Pattern::MatchData]
+ # @param path [Array<String>] the path of this request, built out from each nested
+ # ActionDispatch::Journey::Path::Pattern::MatchData
+ # @param route [::ActionDispatch::Journey::Route]
+ # @param original_url [String] the full url of this request, including the mount
+ # @return [Contrast::Api::Dtm::RouteCoverage, nil]
+ def mounted_sinatra_route request, match, path, route, original_url
+ new_req = unmounted_route(request, match, path)
+ Contrast::Framework::Sinatra::Support.current_route(new_req, route.app.app, original_url)
+ end
+
+ # @param request[Contrast::Agent::Request]
+ # @param match [ActionDispatch::Journey::Path::Pattern::MatchData]
+ # @param path [Array<String>] the path of this request, built out from each nested
+ # ActionDispatch::Journey::Path::Pattern::MatchData
+ # @param route [::ActionDispatch::Journey::Route]
+ # @param original_url [String] the full url of this request, including the mount
+ # @return [Contrast::Api::Dtm::RouteCoverage, nil]
+ def mounted_grape_route request, match, path, route, original_url
+ new_req = unmounted_route(request, match, path)
+ Contrast::Framework::Grape::Support.current_route(new_req, route.app.app, original_url)
+ end
+
+ # Create a request copied from current request, but with the base path removed from path_info, as that's
+ # the mount.
+ #
+ # @param request[Contrast::Agent::Request]
+ # @param match []
+ # @param path [String] the path of this request
+ # @return [::ActionDispatch::Request]
+ def unmounted_route request, match, path
+ new_req = ::ActionDispatch::Request.new(request.env)
+ new_req.path_info = new_req.path_info.gsub((path << match).join, '')
+ new_req
end
end
end
end
end