2015-04-17 00:28:38 +00:00
|
|
|
#!/usr/bin/env ruby
|
|
|
|
|
2016-08-17 21:18:41 +00:00
|
|
|
begin
|
|
|
|
require 'thor'
|
|
|
|
rescue LoadError => e
|
|
|
|
puts "Thor is required. Please install the gem with development dependencies."
|
|
|
|
exit 1
|
|
|
|
end
|
|
|
|
|
2015-04-17 00:28:38 +00:00
|
|
|
require 'open-uri'
|
|
|
|
require 'google/apis/discovery_v1'
|
|
|
|
require 'logger'
|
2020-04-13 16:09:58 +00:00
|
|
|
require 'psych'
|
2015-04-17 00:28:38 +00:00
|
|
|
|
|
|
|
module Google
|
|
|
|
class ApiGenerator < Thor
|
2016-02-25 21:06:16 +00:00
|
|
|
|
|
|
|
def self.exit_on_failure?
|
|
|
|
true
|
|
|
|
end
|
|
|
|
|
2015-04-17 00:28:38 +00:00
|
|
|
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'
|
2017-08-25 18:31:03 +00:00
|
|
|
method_options url: :array, file: :array, from_discovery: :boolean, preferred_only: :boolean,
|
2020-12-31 22:55:59 +00:00
|
|
|
verbose: :boolean, names: :string, names_out: :string, api: :string,
|
|
|
|
clean: :boolean, spot_check: :boolean
|
2015-04-17 00:28:38 +00:00
|
|
|
def gen(dir)
|
2016-02-25 21:06:16 +00:00
|
|
|
ensure_active_support
|
|
|
|
require 'google/apis/generator'
|
|
|
|
|
2015-04-17 00:28:38 +00:00
|
|
|
self.destination_root = dir
|
|
|
|
Google::Apis.logger.level = Logger::DEBUG if options[:verbose]
|
2020-10-15 19:21:50 +00:00
|
|
|
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]
|
2015-04-17 00:28:38 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
desc 'list', 'List public APIs'
|
|
|
|
method_options verbose: :boolean, preferred_only: :boolean
|
|
|
|
def list
|
|
|
|
Google::Apis.logger.level = Logger::DEBUG if options[:verbose]
|
2020-10-15 19:21:50 +00:00
|
|
|
discovery_api_list.each do |api|
|
2015-04-17 00:28:38 +00:00
|
|
|
say sprintf('%s - %s', api.id, api.description).strip unless options[:preferred_only] && !api.preferred?
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
no_commands do
|
2020-10-15 19:21:50 +00:00
|
|
|
def generate_from_url(urls, first_only: false)
|
|
|
|
count = 0
|
2015-12-15 01:41:50 +00:00
|
|
|
Array(urls).each do |url|
|
2017-08-25 23:44:26 +00:00
|
|
|
begin
|
|
|
|
json = discovery.http(:get, url)
|
2020-05-05 18:23:46 +00:00
|
|
|
rescue Google::Apis::Error
|
2017-08-25 23:44:26 +00:00
|
|
|
warn sprintf('Failed request, skipping %s', url)
|
|
|
|
next
|
|
|
|
end
|
2015-12-15 01:41:50 +00:00
|
|
|
generate_api(json)
|
2020-10-15 19:21:50 +00:00
|
|
|
return 1 if first_only
|
|
|
|
count += 1
|
2015-12-15 01:41:50 +00:00
|
|
|
end
|
2020-10-15 19:21:50 +00:00
|
|
|
count
|
2015-04-17 00:28:38 +00:00
|
|
|
end
|
|
|
|
|
2015-12-15 01:41:50 +00:00
|
|
|
def generate_from_file(files)
|
|
|
|
Array(files).each do |file|
|
|
|
|
File.open(file) do |f|
|
|
|
|
generate_api(f.read)
|
|
|
|
end
|
2015-04-17 00:28:38 +00:00
|
|
|
end
|
2020-10-15 19:21:50 +00:00
|
|
|
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
|
2015-04-17 00:28:38 +00:00
|
|
|
end
|
|
|
|
|
2017-08-25 18:31:03 +00:00
|
|
|
def generate_from_discovery(preferred_only: false)
|
2015-04-17 00:28:38 +00:00
|
|
|
say 'Fetching API list'
|
2020-10-15 19:21:50 +00:00
|
|
|
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}"
|
2020-04-13 16:09:58 +00:00
|
|
|
elsif (preferred_only && !api.preferred?)
|
2015-04-17 00:28:38 +00:00
|
|
|
say sprintf('Ignoring disoverable API %s', api.id)
|
2017-08-25 18:31:03 +00:00
|
|
|
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)
|
2020-10-15 19:21:50 +00:00
|
|
|
generate_from_url([discovery_rest_url, api.discovery_rest_url], first_only: true)
|
|
|
|
count += 1
|
2015-04-17 00:28:38 +00:00
|
|
|
end
|
|
|
|
end
|
2020-10-15 19:21:50 +00:00
|
|
|
count
|
|
|
|
end
|
|
|
|
|
|
|
|
def clean_from_discovery
|
|
|
|
count = 0
|
|
|
|
apis = discovery_api_list.map { |api| "#{api.name.underscore}_#{api.version.tr '.', '_'}" }
|
2020-12-31 22:55:59 +00:00
|
|
|
return 0 unless File.directory?("#{destination_root}/google/apis")
|
2020-10-15 19:21:50 +00:00
|
|
|
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
|
2015-04-17 00:28:38 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def generate_api(json)
|
2020-12-31 22:55:59 +00:00
|
|
|
result = generator.render(json)
|
|
|
|
files = updater.analyze(destination_root, result)
|
2015-04-17 00:28:38 +00:00
|
|
|
files.each do |file, content|
|
|
|
|
create_file(file) { |*| content }
|
|
|
|
end
|
2020-12-31 22:55:59 +00:00
|
|
|
run_spot_check(result.gem_name) if !files.empty? && options[:spot_check]
|
|
|
|
end
|
|
|
|
|
|
|
|
def run_spot_check(gem_name)
|
|
|
|
Dir.chdir(File.join(destination_root, gem_name)) do
|
2021-01-01 01:13:48 +00:00
|
|
|
check_proc = Proc.new do
|
|
|
|
system("bundle install") or error_and_exit("Failed to install bundle for newly generated library")
|
|
|
|
system("bundle exec rake ci") or error_and_exit("Failed to run spot check for newly generated library")
|
|
|
|
end
|
|
|
|
if defined?(Bundler)
|
|
|
|
if Bundler.respond_to?(:with_unbundled_env)
|
|
|
|
Bundler.with_unbundled_env(&check_proc)
|
|
|
|
else
|
|
|
|
Bundler.with_clean_env(&check_proc)
|
|
|
|
end
|
|
|
|
else
|
|
|
|
check_proc.call
|
|
|
|
end
|
2020-12-31 22:55:59 +00:00
|
|
|
end
|
2015-04-17 00:28:38 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def discovery
|
|
|
|
@discovery ||= Discovery::DiscoveryService.new
|
|
|
|
end
|
|
|
|
|
2020-10-15 19:21:50 +00:00
|
|
|
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
|
|
|
|
|
2015-04-17 00:28:38 +00:00
|
|
|
def generator
|
2019-05-10 19:57:46 +00:00
|
|
|
@generator ||= Google::Apis::Generator.new(api_names: options[:names], api_names_out: options[:names_out])
|
2015-04-17 00:28:38 +00:00
|
|
|
end
|
2016-02-25 21:06:16 +00:00
|
|
|
|
2020-12-31 22:55:59 +00:00
|
|
|
def updater
|
|
|
|
@updater ||= begin
|
|
|
|
require 'google/apis/generator/updater'
|
|
|
|
Google::Apis::Generator::Updater.new
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-04-13 16:09:58 +00:00
|
|
|
def api_list_config
|
2020-12-22 23:10:52 +00:00
|
|
|
@api_list_config ||= Psych.load_file(__dir__ + "/../../api_list_config.yaml")
|
2020-04-13 16:09:58 +00:00
|
|
|
end
|
|
|
|
|
2016-02-25 21:06:16 +00:00
|
|
|
def ensure_active_support
|
|
|
|
begin
|
2020-10-15 19:21:50 +00:00
|
|
|
require 'active_support'
|
2016-02-25 21:06:16 +00:00
|
|
|
require 'active_support/inflector'
|
2020-10-15 19:21:50 +00:00
|
|
|
require 'active_support/core_ext'
|
2016-02-25 21:06:16 +00:00
|
|
|
rescue LoadError => e
|
2020-12-31 22:55:59 +00:00
|
|
|
error_and_exit('ActiveSupport is required. Please run:', 'gem install activesupport')
|
2016-02-25 21:06:16 +00:00
|
|
|
end
|
|
|
|
end
|
2020-12-31 22:55:59 +00:00
|
|
|
|
|
|
|
def error_and_exit(*messages)
|
|
|
|
messages.each { |msg| error(msg) }
|
|
|
|
exit 1
|
|
|
|
end
|
2015-04-17 00:28:38 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
Google::ApiGenerator.start(ARGV)
|