#!/usr/bin/env ruby begin require 'thor' rescue LoadError => e puts "Thor is required. Please install the gem with development dependencies." exit 1 end require 'open-uri' require 'google/apis/discovery_v1' require 'logger' require 'psych' module Google class ApiGenerator < Thor def self.exit_on_failure? true end include Thor::Actions Google::Apis::ClientOptions.default.application_name = "generate-api" Google::Apis::ClientOptions.default.application_version = Google::Apis::VERSION Discovery = Google::Apis::DiscoveryV1 desc 'gen OUTDIR', 'Generate ruby API from an API description' method_options url: :array, file: :array, from_discovery: :boolean, preferred_only: :boolean, verbose: :boolean, names: :string, names_out: :string, api: :string, clean: :boolean def gen(dir) ensure_active_support require 'google/apis/generator' self.destination_root = dir Google::Apis.logger.level = Logger::DEBUG if options[:verbose] count = 0 count += generate_from_url(options[:url]) if options[:url] count += generate_from_file(options[:file]) if options[:file] count += generate_specific_apis(options[:api]) if options[:api] count += generate_from_discovery(preferred_only: options[:preferred_only]) if options[:from_discovery] count += clean_from_discovery if options[:clean] create_file(options[:names_out]) { |*| generator.dump_api_names } if count > 0 && options[:names_out] end desc 'list', 'List public APIs' method_options verbose: :boolean, preferred_only: :boolean def list Google::Apis.logger.level = Logger::DEBUG if options[:verbose] discovery_api_list.each do |api| say sprintf('%s - %s', api.id, api.description).strip unless options[:preferred_only] && !api.preferred? end end no_commands do def generate_from_url(urls, first_only: false) count = 0 Array(urls).each do |url| begin json = discovery.http(:get, url) rescue Google::Apis::Error warn sprintf('Failed request, skipping %s', url) next end generate_api(json) return 1 if first_only count += 1 end count end def generate_from_file(files) Array(files).each do |file| File.open(file) do |f| generate_api(f.read) end end Array(files).size end def generate_specific_apis(apis) discovery_apis = discovery_api_list.each_with_object({}) do |api, hash| hash["#{api.name}.#{api.version}"] = api end paused_apis = Array(api_list_config["pause"]) count = 0 Array(apis).each do |name_version| api = discovery_apis[name_version] if api.nil? say "API #{api} is not in the discovery list." elsif paused_apis.include? name_version say "Ignoring paused API #{api.name} #{api.version}" else discovery_rest_url = "https://raw.githubusercontent.com/googleapis/discovery-artifact-manager/master/discoveries/#{api.name}.#{api.version}.json" say sprintf('Loading %s, version %s from %s', api.name, api.version, discovery_rest_url) generate_from_url([discovery_rest_url, api.discovery_rest_url], first_only: true) count += 1 end end count end def generate_from_discovery(preferred_only: false) say 'Fetching API list' paused_apis = Array(api_list_config["pause"]) count = 0 discovery_api_list.each do |api| if paused_apis.include? "#{api.name}.#{api.version}" say "Ignoring paused API #{api.name} #{api.version}" elsif (preferred_only && !api.preferred?) say sprintf('Ignoring disoverable API %s', api.id) else # The Discovery service may returned cached versions of a Discovery document that are # not the most recent revision. That means that subsequent requests to the same # Discovery document URL can return different documents. The # `discovery-artifact-manager` repo always holds the most recent revision, so it's # easier to use that document than to force revision checking against the URL returned # by the Discovery index. discovery_rest_url = "https://raw.githubusercontent.com/googleapis/discovery-artifact-manager/master/discoveries/#{api.name}.#{api.version}.json" say sprintf('Loading %s, version %s from %s', api.name, api.version, discovery_rest_url) generate_from_url([discovery_rest_url, api.discovery_rest_url], first_only: true) count += 1 end end count end def clean_from_discovery count = 0 apis = discovery_api_list.map { |api| "#{api.name.underscore}_#{api.version.tr '.', '_'}" } Dir.chdir("#{destination_root}/google/apis") do Dir.glob("*.rb").each do |filename| filename = File.basename(filename, ".rb") unless apis.include? filename FileUtils.rm_r filename FileUtils.rm "#{filename}.rb" count += 1 end end end count end def generate_api(json) files = generator.render(json) files.each do |file, content| create_file(file) { |*| content } end end def discovery @discovery ||= Discovery::DiscoveryService.new end def discovery_api_list @discovery_api_list ||= begin items = discovery.list_apis.items excluded = Array(api_list_config["exclude"]) items.delete_if { |item| excluded.include? "#{item.name}.#{item.version}" } Array(api_list_config["include"]).each do |include_hash| include_hash.symbolize_keys! include_item = Discovery::DirectoryList::Item.new(**include_hash) items << include_item unless items.any? { |item| item.name == include_item.name && item.version == include_item.version } end items end end def generator @generator ||= Google::Apis::Generator.new(api_names: options[:names], api_names_out: options[:names_out]) end def api_list_config @api_list_config ||= Psych.load_file(__dir__ + "/../api_list_config.yaml") end def ensure_active_support begin require 'active_support' require 'active_support/inflector' require 'active_support/core_ext' rescue LoadError => e error 'ActiveSupport is required, please run:' error 'gem install activesupport' exit 1 end end end end end Google::ApiGenerator.start(ARGV)