From 0985317e01998e7346565671ba5cfa4c5ab59448 Mon Sep 17 00:00:00 2001 From: Lex Alexander Date: Tue, 27 Oct 2020 18:45:15 -0700 Subject: [PATCH] feat(Coconut): Improve Error Handling and Add GET/POST methods --- lib/coconutrb.rb | 167 ++++++++++++++++++++++++++--------------- test/coconutrb_test.rb | 39 +++++----- 2 files changed, 128 insertions(+), 78 deletions(-) diff --git a/lib/coconutrb.rb b/lib/coconutrb.rb index 5f01677..471dc95 100644 --- a/lib/coconutrb.rb +++ b/lib/coconutrb.rb @@ -13,106 +13,102 @@ class Error < RuntimeError; end def self.submit(config_content, api_key=nil) api_key ||= API_KEY uri = URI("#{COCONUT_URL}/v1/job") - headers = {"User-Agent" => USER_AGENT, "Content-Type" => "text/plain", "Accept" => "application/json"} - - req = Net::HTTP::Post.new(uri.path, headers) - req.basic_auth api_key, '' - req.body = config_content - - response = Net::HTTP.start(uri.hostname, uri.port, :use_ssl => uri.scheme.include?("https")) do |http| - http.request(req) - end - - return MultiJson.decode(response.body) + post(uri.path, config_content, api_key) end def self.submit!(config_content, opts={}) result = submit(config_content, opts) - if result["status"] == "error" + if (result.has_key?("error") && !result["error"].empty?) || unsuccessful_status(response['status_code']) raise Error, "#{result["message"]} (#{result["error_code"]})" else return result end end + def self.unsuccessful_status(status) + !status.between?(200, 299) + end + def self.get(path, api_key=nil) api_key ||= API_KEY uri = URI("#{COCONUT_URL}#{path}") + req = Net::HTTP::Get.new(uri.path, default_headers) + req.basic_auth(api_key, '') + call(uri, req) + end + + def self.default_headers headers = {"User-Agent" => USER_AGENT, "Content-Type" => "text/plain", "Accept" => "application/json"} + end - req = Net::HTTP::Get.new(uri.path, headers) - req.basic_auth api_key, '' + def self.post(path, config_content, api_key=nil) + api_key ||= API_KEY + uri = URI("#{COCONUT_URL}#{path}") + req = Net::HTTP::Post.new(uri.path, default_headers) + req.basic_auth(api_key, '') + req.body = config_content + call(uri, req) + end - response = Net::HTTP.start(uri.hostname, uri.port, :use_ssl => uri.scheme.include?("https")) do |http| - http.request(req) + def self.parse_response(response) + response_code = response.code.to_i + begin + json = MultiJson.decode(response.body) + not_successful_response = (json['status'] == "error" || !response_code.between?(200, 299)) + if not_successful_response + {"status" => response.code, "error" => json["message"], + "error_code" => json["error_code"], + "status_code" => response_code + } + else + json.merge({"status_code" => response_code}) + end + rescue => e + {"status" => response.code, "error" => e.to_s, "status_code" => response_code} end + end - if response.code.to_i != 200 - return nil - else - return MultiJson.decode(response.body) + def self.call(uri, req) + response = Net::HTTP.start(uri.hostname, uri.port, :use_ssl => uri.scheme.include?("https")) do |http| + http.request(req) end + parse_response(response) end + def self.config(options={}) - if conf_file = options[:conf] - raise Error, "Config file `#{conf_file}' not found" if ! File.exists?(conf_file) - conf = File.read(conf_file).strip.split("\n") + if options[:conf] + raise Error, "Config file `#{options[:conf]}' not found" if !File.exists?(options[:conf]) + conf = File.read(options[:conf]).strip.split("\n") else conf = [] end + conf = add_vars_to_conf(options, conf) + conf = add_source_to_conf(options, conf) + conf = add_webhook_to_conf(options, conf) + conf = add_api_version_to_conf(options, conf) + conf = add_outputs_to_conf(options, conf) + conf = sort_conf(conf) + end - if vars = options[:vars] - vars.each do |name,value| - conf << "var #{name} = #{value}" - end - end - - if source = options[:source] - conf << "set source = #{source}" - end - - if webhook = options[:webhook] - if webhook.is_a?(Hash) - webhook = hash_params_to_string(webhook) - end - - conf << "set webhook = #{webhook}" - end - - if api_version = options[:api_version] - conf << "set api_version = #{api_version}" - end - - if outputs = options[:outputs] - outputs.each do |format, cdn| - if cdn.is_a?(Hash) - cdn = hash_params_to_string(cdn) - end - - conf << "-> #{format} = #{cdn}" - end - end - + def self.sort_conf(conf) new_conf = [] - new_conf.concat conf.select{|l| l.start_with?("var")}.sort new_conf << "" new_conf.concat conf.select{|l| l.start_with?("set")}.sort new_conf << "" new_conf.concat conf.select{|l| l.start_with?("->")}.sort - - return new_conf.join("\n") + new_conf.join("\n") end def self.hash_params_to_string(params) - params.map{|k,v| + params.map do |k,v| if k.to_s == "url" "#{v}" else "#{k}=#{v}" end - }.join(", ") + end.join(", ") end class Job @@ -132,4 +128,55 @@ def self.get_metadata_for(jid, source_or_output, api_key=nil) Coconut.get("/v1/metadata/jobs/#{jid}/#{source_or_output}", api_key) end end + + private + def self.add_vars_to_conf(options, conf) + return conf if !options[:vars] + vars = options[:vars] + vars.each do |name,value| + conf << "var #{name} = #{value}" + end + conf + end + + def self.add_source_to_conf(options, conf) + return conf if !options[:source] + if options[:source] + conf << "set source = #{options[:source]}" + end + conf + end + + def self.add_webhook_to_conf(options, conf) + return conf if !options[:webhook] + webhook_copy = options[:webhook] + if webhook_copy + if webhook_copy.is_a?(Hash) + webhook_copy = hash_params_to_string(webhook_copy) + end + conf << "set webhook = #{webhook_copy}" + end + conf + end + + def self.add_api_version_to_conf(options, conf) + return conf if !options[:api_version] + if options[:api_version] + conf << "set api_version = #{options[:api_version]}" + end + conf + end + + def self.add_outputs_to_conf(options, conf) + return conf if !options[:outputs] + if options[:outputs] + options[:outputs].each do |format, cdn| + if cdn.is_a?(Hash) + cdn = hash_params_to_string(cdn) + end + conf << "-> #{format} = #{cdn}" + end + end + conf + end end \ No newline at end of file diff --git a/test/coconutrb_test.rb b/test/coconutrb_test.rb index 0a85a1e..8a16449 100644 --- a/test/coconutrb_test.rb +++ b/test/coconutrb_test.rb @@ -17,8 +17,8 @@ def test_submit_job ) job = Coconut.submit(conf) - assert_equal "processing", job["status"] - assert job["id"] > 0 + assert_equal("processing", job["status"]) + assert(job["id"] > 0) end def test_submit_bad_config_should_raise @@ -37,8 +37,8 @@ def test_submit_config_with_api_key ) job = Coconut.submit(conf, "k-4d204a7fd1fc67fc00e87d3c326d9b75") - assert_equal "error", job["status"] - assert_equal "authentication_failed", job["error_code"] + assert_equal("Authentication failed, check your API key", job["error"]) + assert_equal("authentication_failed", job["error_code"]) end def test_submit_bad_config_should_not_raise @@ -47,8 +47,9 @@ def test_submit_bad_config_should_not_raise ) job = Coconut.submit(conf) - assert_equal "error", job["status"] - assert_equal "config_not_valid", job["error_code"] + assert_equal("400", job['status']) + assert_equal("The config file must specify the `source' video location, a `webhook` URL and at least 1 output", job["error"]) + assert_equal("config_not_valid", job["error_code"]) end def test_generate_full_config_with_no_file @@ -80,7 +81,7 @@ def test_generate_full_config_with_no_file "-> webm = $s3/vid.webm" ].join("\n") - assert_equal generated, config + assert_equal(generated, config) end def test_generate_config_with_file @@ -103,7 +104,7 @@ def test_generate_config_with_file "-> mp4 = $s3/$vid.mp4", ].join("\n") - assert_equal generated, config + assert_equal(generated, config) File.delete("coconut.conf") end @@ -116,8 +117,8 @@ def test_submit_file :source => "https://s3-eu-west-1.amazonaws.com/files.coconut.co/test.mp4", :vars => {:vid => 1234, :user => 5098} ) - assert_equal "processing", job["status"] - assert job["id"] > 0 + assert_equal("processing", job["status"]) + assert(job["id"] > 0) File.delete("coconut.conf") end @@ -127,9 +128,9 @@ def test_set_api_key_in_job_options :api_key => "k-4d204a7fd1fc67fc00e87d3c326d9b75", :source => "https://s3-eu-west-1.amazonaws.com/files.coconut.co/test.mp4", ) - - assert_equal "error", job["status"] - assert_equal "authentication_failed", job["error_code"] + assert_equal("401", job["status"]) + assert_equal("Authentication failed, check your API key", job["error"]) + assert_equal("authentication_failed", job["error_code"]) end def test_get_job_info @@ -142,11 +143,13 @@ def test_get_job_info job = Coconut.submit(conf) info = Coconut::Job.get(job["id"]) - assert_equal info["id"], job["id"] + assert_equal(info["id"], job["id"]) end def test_get_not_found_job_returns_nil - assert_nil Coconut::Job.get(1000) + job = Coconut::Job.get(1000) + assert_equal('404', job['status']) + assert_equal('compile error', job['error']) end def test_set_api_version @@ -166,7 +169,7 @@ def test_set_api_version "-> mp4 = $s3/vid.mp4", ].join("\n") - assert_equal generated, config + assert_equal(generated, config) end def test_get_all_metadata @@ -180,7 +183,7 @@ def test_get_all_metadata sleep 4 metadata = Coconut::Job.get_all_metadata(job["id"]) - assert_not_nil metadata + assert_not_nil(metadata) end def test_get_metadata_for_source @@ -194,7 +197,7 @@ def test_get_metadata_for_source sleep 4 metadata = Coconut::Job.get_metadata_for(job["id"], :source) - assert_not_nil metadata + assert_not_nil(metadata) end def test_cdn_parameters_as_hash