ruby-grape/grape-swagger

View on GitHub
lib/grape-swagger/rake/oapi_tasks.rb

Summary

Maintainability
A
0 mins
Test Coverage
# frozen_string_literal: true

require 'rake'
require 'rake/tasklib'
require 'rack/test'

module GrapeSwagger
  module Rake
    class OapiTasks < ::Rake::TaskLib
      include Rack::Test::Methods

      attr_reader :oapi

      def initialize(api_class)
        super()

        if api_class.is_a? String
          @api_class_name = api_class
        else
          @api_class = api_class
        end

        define_tasks
      end

      private

      def api_class
        @api_class ||= @api_class_name.constantize
      end

      def define_tasks
        namespace :oapi do
          fetch
          validate
        end
      end

      # tasks
      #
      # get swagger/OpenAPI documentation
      def fetch
        desc 'generates OpenApi documentation …
          params (usage: key=value):
          store    – save as JSON file, default: false            (optional)
          resource - if given only for that it would be generated (optional)'
        task fetch: :environment do
          # :nocov:
          urls_for(api_class).each do |url|
            make_request(url)

            save_to_file? ? File.write(file(url), @oapi) : $stdout.print(@oapi)
          end

          # :nocov:
        end
      end

      # validates swagger/OpenAPI documentation
      def validate
        desc 'validates the generated OpenApi file …
          params (usage: key=value):
          resource - if given only for that it would be generated (optional)'
        task validate: :environment do
          # :nocov:
          ENV['store'] = 'true'
          ::Rake::Task['oapi:fetch'].invoke
          exit if error?

          urls_for(api_class).each do |url|
            @output = system "swagger-cli validate #{file(url)}"

            FileUtils.rm(
              file(url)
            )
          end

          $stdout.puts 'install swagger-cli with `npm install swagger-cli -g`' if @output.nil?
          # :nocov:
        end
      end

      # helper methods
      #
      # rubocop:disable Style/StringConcatenation
      def make_request(url)
        get url

        @oapi = JSON.pretty_generate(
          JSON.parse(last_response.body, symolize_names: true)
        ) + "\n"
      end
      # rubocop:enable Style/StringConcatenation

      def urls_for(api_class)
        api_class.routes
                 .map(&:path)
                 .select { |e| e.include?('doc') }
                 .reject { |e| e.include?(':name') }
                 .map { |e| format_path(e) }
                 .map { |e| [e, ENV.fetch('resource', nil)].join('/').chomp('/') }
      end

      def format_path(path)
        oapi_route = api_class.routes.select { |e| e.path == path }.first
        path = path.sub(/\(\.\w+\)$/, '').sub(/\(\.:\w+\)$/, '')
        path.sub(':version', oapi_route.version.to_s)
      end

      def save_to_file?
        ENV['store'].present? && !error?
      end

      def error?
        JSON.parse(@oapi).keys.first == 'error'
      end

      def file(url)
        api_version = url.split('/').last

        name = if ENV['store'] == 'true' || ENV['store'].blank?
                 "swagger_doc_#{api_version}.json"
               else
                 ENV['store'].sub('.json', "_#{api_version}.json")
               end

        File.join(Dir.getwd, name)
      end

      def app
        api_class.new
      end
    end
  end
end