bogdanRada/washout_builder

View on GitHub
app/controllers/washout_builder/washout_builder_controller.rb

Summary

Maintainability
A
2 hrs
Test Coverage
require_relative '../../../lib/washout_builder/document/generator'
module WashoutBuilder
  # controller that is used to prit all available services or print the documentation for a specific service
  class WashoutBuilderController < ActionController::Base
    protect_from_forgery
    before_action :check_env_available

    # Will show all api services if no name parameter is receiverd
    # If a name parameter is present will try to use that and find a controller
    # that was that name by camelcasing the name .
    # IF a name is provided will show the documentation page for that controller
    # @see #all_services
    # @see WashoutBuilder::Document::Generator#new
    #
    # @return [void]
    #
    # @api public
    def all
      params[:name] = env_controller_path if env_controller_path.present?
      find_all_routes
      route_details = params[:name].present? ? controller_is_a_service?(params[:name]) : nil
      if route_details.present? && defined?(controller_class(params[:name]))
        @document = WashoutBuilder::Document::Generator.new(route_details, controller_class(params[:name]).controller_path)
        @file_to_serve = 'wash_with_html/doc'
        render_html(@file_to_serve)
      else
        @services = all_services
        @file_to_serve = 'wash_with_html/all_services'
        render_html(@file_to_serve)
      end
    end

    private

    def env_controller_path
      request.env['washout_builder.controller_path']
    end


    def render_html(path, options= { layout: false, content_type: 'text/html' })
      options = options.merge(template: path)
      respond_to do |format|
        format.json { render(options)  }
        render_html_format_all(format, options)
      end
    end

    def render_html_format_all(format, options)
      format.all do
        response.content_type = 'text/html; charset=utf-8'
        render(*[options.merge(formats: [:html])])
      end
    end

    # tries to find all services by searching through the rails controller
    # and returns their namespace, endpoint and a documentation url
    # @see map_controllers
    #
    #
    # @return [Hash] options The hash that contains all information about available services
    # @option options [String] :service_name (@see #controller_naming)  The name of the controller that is a  soap servive
    # @option options [String]:namespace (@see #service_namespace ) The namespace of the soap service for that controller
    # @option options [String] :endpoint (@see #service_endpoint ) The endpoint of the service controller
    # @option options [String] :documentation_url (@see #service_documentation_url ) The url where the documentation can be seen in HTML
    # for that service controller
    #
    # @api private
    def all_services
      @map_controllers = map_controllers { |hash| hash }
      @map_controllers.blank? ? [] : @map_controllers.map do |hash|
        controller_name = hash[:route].present? && hash[:route].respond_to?(:defaults) ? hash[:route].defaults[:controller] : nil
        if controller_name.present?
          {
              'service_name' => controller_naming(controller_name),
              'namespace' => service_namespace(hash, controller_name),
              'endpoint' => service_endpoint(hash, controller_name),
              'documentation_url' => service_documentation_url(hash, controller_name)
          }
        end
      end.uniq{|hash| hash['service_name'] }
    end

    def generate_wsdl_action
      '_generate_wsdl'
    end

    # the way of converting from controller string in downcase in camelcase
    # @param [String] controller  The controller name in downcase letter
    #
    # @return [String] The controller name in camelcase letters
    #
    # @api private
    def controller_naming(controller)
      controller.camelize
    end

    # checking if a route has the action for generating WSDL
    # @param [ActionDispatch::Journey::Route] route The route that is used to check if can respond to _generate_wsdl action
    #
    # @return [Boolean] Returns true if the route can respond to _generate_wsdl action
    #
    # @api private
    def route_can_generate_wsdl?(route)
      route.defaults[:action] == generate_wsdl_action
    end

    def find_all_routes
      # get the controller and set the action for redirection
      engines = [Rails.application]
      engines.concat(::Rails::Engine.subclasses)

      routes = engines.each_with_object([]) do |engine, routes_array|
        engine_route = Rails.application.routes.named_routes[engine.engine_name]
        routes_hash_array = engine.routes.routes.map { |route|
          {
              engine: engine,
              route_set: engine.routes,
              route: route,
              mounted_at: engine_route.blank? ? nil : engine_route.path.spec.to_s
          }
        }

        routes_array.concat(routes_hash_array)
      end

      #routes = routes.sort_by { |hash| hash[:route].precedence }
      @routes = routes
      @routes
    end

    # method for getting all controllers that have the generate wsdl action or finding out
    # if a single controller is a soap service
    # @see #route_can_generate_wsdl?
    #
    # @param [String] action The action is used to collect or find a particular route . Can only be *map* or *detect*
    #
    # @yield [ActionDispatch::Journey::Route] yield each route to the block while iterating through the routes
    #
    # @return [ActionDispatch::Journey::Route, Array<ActionDispatch::Journey::Route>] Can return either a collection of routes or a single route depending on the action
    #
    # @api private
    def map_controllers(action = 'select')
      res = @routes.send(action) do |hash|
        if hash[:route].present? && route_can_generate_wsdl?(hash[:route])
          yield hash if hash.present? && block_given?
        end
      end
      res = res.compact.uniq{|hash| hash[:route] } if action == 'select'
      res
    end

    # checking if a controller is a soap service
    # @see #map_controllers
    # @see #controller_naming
    #
    # @param [String] controller The controller that is used to check if it is soap service
    #
    # @return [Boolean] Returns true if we find a route that can generate wsdl and the name of the route matches the name of the controller
    #
    # @api private
    def controller_is_a_service?(controller)
      map_controllers('detect') do |hash|
        if hash[:route].present? && hash[:route].respond_to?(:defaults)
          controller_naming(hash[:route].defaults[:controller]) == controller_naming(controller)
        end
      end
    end

    # getting the controller class from the controller string
    # @see #controller_naming
    #
    # @param [String] controller The name of the controller
    # @return [Class] the original controller class name
    # @api private
    def controller_class(controller)
      controller = controller.gsub(/\/+/, '/')
      controller_naming("#{controller}_controller").constantize
    end

    def route_helpers(hash)
      hash[:route_set].url_helpers
    end

    # retrieves the service namespace
    # @see #controller_class
    #
    # the method receives the controlle name than will try to find the class  name
    # of the controller and use the soap configuration of the class to
    # retrive the namespace of the soap service
    #
    # @param [String] controller_name The name of the controller
    # @return [String] The namespace of the soap service that is used in that controller
    #
    # @api private
    def service_namespace(hash, controller_name)
      #controller_class(controller_name).soap_config.namespace
      route_helpers(hash).url_for(controller: controller_name, action: generate_wsdl_action, only_path: true)
    end

    # the endpoint is based on the namespace followed by /action suffix
    # @see #service_namespace
    #
    # @param [String] controller_name The name of the controller
    # @return [String] The endpoint of the web service
    # @api private
    def service_endpoint(hash, controller_name)
      service_namespace(hash, controller_name).gsub('/wsdl', '/action')
    end

    # constructs the documentation url for a specific web service
    #
    # @param [String] controller_name The name of the controller
    # @return [String] The documentation url for the web service ( relative to base url)
    # @api private
    def service_documentation_url(hash, controller_name)
      service_namespace(hash, controller_name).gsub('/wsdl', '/soap_doc')
      #"#{washout_builder.root_path}#{controller_naming(controller_name)}"
    end

    def check_env_available
      env_checker = WashoutBuilder::EnvChecker.new(Rails.application)
      raise ActionController::RoutingError unless env_checker.available_for_env?(Rails.env)
    end

  end
end