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