# Licensed to Elasticsearch B.V under one or more agreements. # Elasticsearch B.V licenses this file to you under the Apache 2.0 License. # See the LICENSE file in the project root for more information # encoding: UTF-8 require 'thor' require 'pathname' require 'active_support/core_ext/hash/deep_merge' require 'active_support/inflector' require 'multi_json' require 'coderay' require 'pry' module Elasticsearch module API # A command line application based on [Thor](https://github.com/wycats/thor), # which will read the JSON API spec file(s), and generate # the Ruby source code (one file per API endpoint) with correct # module namespace, method names, and RDoc documentation, # as well as test files for each endpoint. # # Currently it only generates Ruby source, but can easily be # extended and adapted to generate source code for other # programming languages. # class SourceGenerator < Thor namespace 'api:code' include Thor::Actions __root = Pathname( File.expand_path('../../..', __FILE__) ) desc "generate ", "Generate source code and tests from the REST API JSON specification" method_option :language, default: 'ruby', desc: 'Programming language' method_option :force, type: :boolean, default: false, desc: 'Overwrite the output' method_option :verbose, type: :boolean, default: false, desc: 'Output more information' method_option :input, default: File.expand_path('../../../../tmp/elasticsearch/rest-api-spec/api/**/*.json', __FILE__), desc: 'Path to directory with JSON API specs' method_option :output, default: File.expand_path('../../../tmp/out', __FILE__), desc: 'Path to output directory' def generate(*files) self.class.source_root File.expand_path('../', __FILE__) @input = Pathname(options[:input]) @output = Pathname(options[:output]) # -- Test helper copy_file "templates/ruby/test_helper.rb", @output.join('test').join('test_helper.rb') if options[:language] == 'ruby' Dir[@input].each do |file| next if File.basename(file)[0] == '_' @path = Pathname(file) @json = MultiJson.load( File.read(@path) ) @spec = @json.values.first say_status 'json', @path, :yellow @spec['url'] ||= {} @spec['url']['parts'] ||= [] @spec['url']['params'] ||= {} # say_status 'JSON', @spec.inspect, options[:verbose] @full_namespace = @json.keys.first.split('.') @namespace_depth = @full_namespace.size > 0 ? @full_namespace.size-1 : 0 @module_namespace = @full_namespace[0, @namespace_depth] @method_name = @full_namespace.last @http_method = "Elasticsearch::API::HTTP_#{@spec['methods'].first}" @http_path = unless @spec['url']['parts'].empty? @spec['url']['path'] .split('/') .compact .reject { |p| p =~ /^\s*$/ } .map { |p| p =~ /\{/ ? "\#\{arguments[:#{p.tr('{}', '')}]\}" : p } .join('/') .gsub(/^\//, '') else @spec['url']['path'].gsub(/^\//, '') end # -- Ruby files @path_to_file = @output.join('api').join( @module_namespace.join('/') ).join("#{@method_name}.rb") empty_directory @output.join('api').join( @module_namespace.join('/') ) template "templates/#{options[:language]}/method.erb", @path_to_file if options[:verbose] colorized_output = CodeRay.scan_file(@path_to_file, :ruby).terminal lines = colorized_output.split("\n") say_status options[:language].downcase, lines.first + "\n" + lines[1, lines.size].map { |l| ' '*14 + l }.join("\n"), :yellow end # --- Test files @test_directory = @output.join('test/api').join( @module_namespace.join('/') ) @test_file = @test_directory.join("#{@method_name}_test.rb") empty_directory @test_directory template "templates/#{options[:language]}/test.erb", @test_file if options[:verbose] colorized_output = colorized_output = CodeRay.scan_file(@test_file, :ruby).terminal lines = colorized_output.split("\n") say_status options[:language].downcase, lines.first + "\n" + lines[1, lines.size].map { |l| ' '*14 + l }.join("\n"), :yellow say '▬'*terminal_width end end # -- Tree output if options[:verbose] && `which tree > /dev/null 2>&1; echo $?`.to_i < 1 lines = `tree #{@output}`.split("\n") say_status 'tree', lines.first + "\n" + lines[1, lines.size].map { |l| ' '*14 + l }.join("\n") end end private # Create the hierarchy of directories based on API namespaces # def __create_directories(key, value) unless value['documentation'] empty_directory @output.join(key) create_directory_hierarchy *value.to_a.first end end end end end