diff --git a/CHANGELOG.md b/CHANGELOG.md index c7632b2..3f8d91b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +## [0.0.7] + - Added Media API + ## [0.0.3] ### Added diff --git a/config/versions.yml b/config/versions.yml index af5a692..91823fc 100644 --- a/config/versions.yml +++ b/config/versions.yml @@ -8,6 +8,7 @@ buy: commerce: taxonomy: "1.0.0" notifications: "1.0.0" + media: "1_beta.1.0" sell: account: "1.1.0" analytics: "1.0.0" diff --git a/ebay_api.gemspec b/ebay_api.gemspec index 1e90c5e..1b70931 100644 --- a/ebay_api.gemspec +++ b/ebay_api.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |gem| gem.name = "ebay_api" - gem.version = "0.0.6" + gem.version = "0.0.7" gem.author = "Andrew Kozin (nepalez)" gem.email = "andrew.kozin@gmail.com" gem.homepage = "https://github.com/nepalez/sms_aero" diff --git a/lib/ebay_api.rb b/lib/ebay_api.rb index 26afe31..01224e6 100644 --- a/lib/ebay_api.rb +++ b/lib/ebay_api.rb @@ -32,12 +32,13 @@ class << self end option :token - option :site, Site, optional: true - option :language, Language, optional: true - option :charset, Charset, default: proc { "utf-8" } - option :sandbox, true.method(:&), default: proc { false } - option :gzip, true.method(:&), default: proc { false } - option :user_agent, method(:String), optional: true + option :site, Site, optional: true + option :language, Language, optional: true + option :api_subdomain, default: proc { "api" } + option :charset, Charset, default: proc { "utf-8" } + option :sandbox, true.method(:&), default: proc { false } + option :gzip, true.method(:&), default: proc { false } + option :user_agent, method(:String), optional: true validate do next unless language && site @@ -46,9 +47,9 @@ class << self end format "json" - path { "https://api#{".sandbox" if sandbox}.ebay.com/" } + path { "https://#{api_subdomain}#{".sandbox" if sandbox}.ebay.com/" } - middleware { [LogRequest, JSONResponse] } + middleware { [LogRequest, AddVideoIdToBody, ReplaceRequestHeaders, JSONResponse] } security do token_value = token.respond_to?(:call) ? token.call : token @@ -70,7 +71,7 @@ class << self response(204) { true } # https://developer.ebay.com/api-docs/static/handling-error-messages.html - response(400, 401, 409) do |_, _, (data, *)| + response(400, 401, 404, 409, 415) do |_, _, (data, *)| data = data.to_h error = data.dig("errors", 0) || {} code = error["errorId"] diff --git a/lib/ebay_api/middlewares.rb b/lib/ebay_api/middlewares.rb index 31fb13f..8f6d602 100644 --- a/lib/ebay_api/middlewares.rb +++ b/lib/ebay_api/middlewares.rb @@ -2,3 +2,5 @@ require_relative "middlewares/save_to_file_response" require_relative "middlewares/paginated_collection" require_relative "middlewares/log_request" +require_relative "middlewares/add_video_id_to_body" +require_relative "middlewares/replace_request_headers" diff --git a/lib/ebay_api/middlewares/add_video_id_to_body.rb b/lib/ebay_api/middlewares/add_video_id_to_body.rb new file mode 100644 index 0000000..3710743 --- /dev/null +++ b/lib/ebay_api/middlewares/add_video_id_to_body.rb @@ -0,0 +1,22 @@ +class EbayAPI + class AddVideoIdToBody + def initialize(app) + @app = app + end + + def call(env) + status, headers, body = @app.call(env) + + body = if status == 201 && + headers.keys.include?("location") && + env["REQUEST_METHOD"] == "POST" && + env["PATH_INFO"] == "/commerce/media/v1_beta/video/" + [{ "id" => headers["location"][0].split("/").last }] + else + body + end + + [status, headers, body] + end + end +end diff --git a/lib/ebay_api/middlewares/replace_request_headers.rb b/lib/ebay_api/middlewares/replace_request_headers.rb new file mode 100644 index 0000000..163f980 --- /dev/null +++ b/lib/ebay_api/middlewares/replace_request_headers.rb @@ -0,0 +1,16 @@ +class EbayAPI + class ReplaceRequestHeaders + def initialize(app) + @app = app + end + + def call(env) + if env["REQUEST_METHOD"] == "POST" && + env["PATH_INFO"].match?('\A\/commerce\/media\/v1_beta\/video\/[a-z0-9]+\/upload$') + env["HTTP_Variables"]["Content-Type"] = "application/octet-stream" + end + + @app.call(env) + end + end +end diff --git a/lib/ebay_api/operations/commerce.rb b/lib/ebay_api/operations/commerce.rb index cd80922..a026cb7 100644 --- a/lib/ebay_api/operations/commerce.rb +++ b/lib/ebay_api/operations/commerce.rb @@ -9,5 +9,6 @@ class EbayAPI require_relative "commerce/notification" require_relative "commerce/taxonomy" + require_relative "commerce/media" end end diff --git a/lib/ebay_api/operations/commerce/media.rb b/lib/ebay_api/operations/commerce/media.rb new file mode 100644 index 0000000..f351380 --- /dev/null +++ b/lib/ebay_api/operations/commerce/media.rb @@ -0,0 +1,14 @@ +class EbayAPI + scope :commerce do + # + # eBay Commerce Media API + # + # @see https://developer.ebay.com/api-docs/commerce/media/overview.html + # + scope :media do + path { "media/v#{EbayAPI::COMMERCE_MEDIA_VERSION.split('.')[0]}" } + + require_relative "media/video" + end + end +end diff --git a/lib/ebay_api/operations/commerce/media/video.rb b/lib/ebay_api/operations/commerce/media/video.rb new file mode 100644 index 0000000..9e99dd0 --- /dev/null +++ b/lib/ebay_api/operations/commerce/media/video.rb @@ -0,0 +1,18 @@ +class EbayAPI + scope :commerce do + # + # eBay Commerce Media API + # + # @see https://developer.ebay.com/api-docs/commerce/media/overview.html + # + scope :media do + scope :video do + path "video" + + require_relative "video/get" + require_relative "video/create" + require_relative "video/upload" + end + end + end +end diff --git a/lib/ebay_api/operations/commerce/media/video/create.rb b/lib/ebay_api/operations/commerce/media/video/create.rb new file mode 100644 index 0000000..85c512f --- /dev/null +++ b/lib/ebay_api/operations/commerce/media/video/create.rb @@ -0,0 +1,19 @@ +class EbayAPI + scope :commerce do + scope :media do + scope :video do + # @see https://developer.ebay.com/api-docs/commerce/media/resources/video/methods/createVideo + operation :create do + option :payload, proc(&:to_h) # TODO: add model to validate input + + # Create endpoint of Ebay Media API return empty body by it self and id + # of created video is located in location response header. + # We uses AddVideoIdToBody middleware for putting id to body + path { "/" } + http_method :post + body { payload.merge("classification" => ["ITEM"]) } + end + end + end + end +end diff --git a/lib/ebay_api/operations/commerce/media/video/get.rb b/lib/ebay_api/operations/commerce/media/video/get.rb new file mode 100644 index 0000000..b172066 --- /dev/null +++ b/lib/ebay_api/operations/commerce/media/video/get.rb @@ -0,0 +1,15 @@ +class EbayAPI + scope :commerce do + scope :media do + scope :video do + # @see https://developer.ebay.com/api-docs/commerce/media/resources/video/methods/getVideo + operation :get do + option :id, proc(&:to_s) + + path { id } + http_method :get + end + end + end + end +end diff --git a/lib/ebay_api/operations/commerce/media/video/upload.rb b/lib/ebay_api/operations/commerce/media/video/upload.rb new file mode 100644 index 0000000..199d5c3 --- /dev/null +++ b/lib/ebay_api/operations/commerce/media/video/upload.rb @@ -0,0 +1,23 @@ +class EbayAPI + scope :commerce do + scope :media do + scope :video do + # @see https://developer.ebay.com/api-docs/commerce/media/resources/video/methods/uploadVideo + operation :upload do + option :id, proc(&:to_s) + option :file + + path { "#{id}/upload" } + http_method :post + # Upload endpoint of Ebay Media API requires "content-type": "application/octet-stream" + # request header. + # But evil-client hasn't format for "application/octet-stream". + # That's we using "text" format and changing "content-type" header + # in ReplaceRequestHeaders middleware. + format { :text } + body { file } + end + end + end + end +end diff --git a/lib/ebay_api/operations/commerce/taxonomy.rb b/lib/ebay_api/operations/commerce/taxonomy.rb index 8ed0fd6..bfe3a28 100644 --- a/lib/ebay_api/operations/commerce/taxonomy.rb +++ b/lib/ebay_api/operations/commerce/taxonomy.rb @@ -1,9 +1,9 @@ class EbayAPI scope :commerce do # - # eBay Commerce Notifications API + # eBay Commerce Taxonomy API # - # @see https://developer.ebay.com/api-docs/commerce/notification/overview.html + # @see https://developer.ebay.com/api-docs/commerce/taxonomy/overview.html # scope :taxonomy do path { "taxonomy/v#{EbayAPI::COMMERCE_TAXONOMY_VERSION[/^\d+/]}" } diff --git a/lib/ebay_api/operations/commerce/taxonomy/category_tree.rb b/lib/ebay_api/operations/commerce/taxonomy/category_tree.rb index 82afabc..d2ded36 100644 --- a/lib/ebay_api/operations/commerce/taxonomy/category_tree.rb +++ b/lib/ebay_api/operations/commerce/taxonomy/category_tree.rb @@ -1,7 +1,7 @@ class EbayAPI scope :commerce do # - # eBay Commerce Notifications API + # eBay Taxonomy API # # @see https://developer.ebay.com/api-docs/commerce/taxonomy/overview.html # diff --git a/spec/fixtures/commerce/media/video/create/success b/spec/fixtures/commerce/media/video/create/success new file mode 100644 index 0000000..e567690 --- /dev/null +++ b/spec/fixtures/commerce/media/video/create/success @@ -0,0 +1,3 @@ +HTTP/1.1 201 OK +Content-Length: 0 +Location: https://apim.ebay.com/commerce/media/v1_beta/video/92626cdc1820ab8eac2356d1ffffe6d0 diff --git a/spec/fixtures/commerce/media/video/get/success b/spec/fixtures/commerce/media/video/get/success new file mode 100644 index 0000000..910afb3 --- /dev/null +++ b/spec/fixtures/commerce/media/video/get/success @@ -0,0 +1,5 @@ +HTTP/1.1 200 OK +Content-Length: 751 +Content-Type: application/json + +{"videoId": "92626cdc1820ab8eac2356d1ffffe6d0", "size": 3114374, "title": "Test video", "description": "Test video description", "status": "PENDING_UPLOAD", "expirationDate": "2023-08-12T08:41:03Z", "classification": [ "ITEM" ] } diff --git a/spec/fixtures/commerce/media/video/upload/success b/spec/fixtures/commerce/media/video/upload/success new file mode 100644 index 0000000..a919d52 --- /dev/null +++ b/spec/fixtures/commerce/media/video/upload/success @@ -0,0 +1 @@ +HTTP/1.1 200 OK diff --git a/spec/fixtures/commerce/taxonomy/category_tree/get/success b/spec/fixtures/commerce/taxonomy/category_tree/get/success index 5aa3401..6a9ec38 100644 --- a/spec/fixtures/commerce/taxonomy/category_tree/get/success +++ b/spec/fixtures/commerce/taxonomy/category_tree/get/success @@ -2,4 +2,4 @@ HTTP/1.1 200 OK Content-Length: 751 Content-Type: application/json -{"applicableMarketplaceIds": ["MarketplaceIdEnum"], "categoryTreeId": "string", "categoryTreeVersion": "string", "rootCategoryNode": {"category": {"categoryId": "string", "categoryName": "string"}, "categoryTreeNodeLevel": "integer", "childCategoryTreeNodes": [{"category": {"categoryId": "string", "categoryName": "string"}, "categoryTreeNodeLevel": "integer", "childCategoryTreeNodes": [{"category": {"categoryId": "string", "categoryName": "string"}, "categoryTreeNodeLevel": "integer", "childCategoryTreeNodes": [{}], "leafCategoryTreeNode": "boolean", "parentCategoryTreeNodeHref": "string"}], "leafCategoryTreeNode": "boolean", "parentCategoryTreeNodeHref": "string"}], "leafCategoryTreeNode": "boolean", "parentCategoryTreeNodeHref": "string"}} \ No newline at end of file +{"applicableMarketplaceIds": ["MarketplaceIdEnum"], "categoryTreeId": "string", "categoryTreeVersion": "string", "rootCategoryNode": {"category": {"categoryId": "string", "categoryName": "string"}, "categoryTreeNodeLevel": "integer", "childCategoryTreeNodes": [{"category": {"categoryId": "string", "categoryName": "string"}, "categoryTreeNodeLevel": "integer", "childCategoryTreeNodes": [{"category": {"categoryId": "string", "categoryName": "string"}, "categoryTreeNodeLevel": "integer", "childCategoryTreeNodes": [{}], "leafCategoryTreeNode": "boolean", "parentCategoryTreeNodeHref": "string"}], "leafCategoryTreeNode": "boolean", "parentCategoryTreeNodeHref": "string"}], "leafCategoryTreeNode": "boolean", "parentCategoryTreeNodeHref": "string"}} diff --git a/spec/operations/commerce/media/video/create_spec.rb b/spec/operations/commerce/media/video/create_spec.rb new file mode 100644 index 0000000..56bd494 --- /dev/null +++ b/spec/operations/commerce/media/video/create_spec.rb @@ -0,0 +1,32 @@ +RSpec.describe EbayAPI, ".commerce.media.video.create" do + let(:client) { described_class.new(settings) } + let(:scope) { client.commerce.media.video } + let(:settings) { yaml_fixture_file("settings.valid.yml").merge(api_subdomain: "apim") } + let(:url) do + "https://apim.ebay.com/commerce/media/v1_beta/video/" + end + let(:payload) do + { + "size": 3_114_374, + "title": "Test video", + "description": "Test video description" + } + end + + before { stub_request(:post, url).to_return(response) } + + subject do + scope.create(payload: payload) + end + + context "success" do + let(:response) do + open_fixture_file "commerce/media/video/create/success" + end + + it "sends a request" do + expect(subject).to eq({ "id" => "92626cdc1820ab8eac2356d1ffffe6d0" }) + expect(a_request(:post, url)).to have_been_made + end + end +end diff --git a/spec/operations/commerce/media/video/get_spec.rb b/spec/operations/commerce/media/video/get_spec.rb new file mode 100644 index 0000000..c1a3915 --- /dev/null +++ b/spec/operations/commerce/media/video/get_spec.rb @@ -0,0 +1,26 @@ +RSpec.describe EbayAPI, ".commerce.media.video.get" do + let(:client) { described_class.new(settings) } + let(:scope) { client.commerce.media.video } + let(:settings) { yaml_fixture_file("settings.valid.yml").merge(api_subdomain: "apim") } + let(:video_id) { "92626cdc1820ab8eac2356d1ffffe6d0" } + let(:url) do + "https://apim.ebay.com/commerce/media/v1_beta/video/#{video_id}" + end + + before { stub_request(:get, url).to_return(response) } + + subject do + scope.get(id: video_id) + end + + context "success" do + let(:response) do + open_fixture_file "commerce/media/video/get/success" + end + + it "sends a request" do + subject + expect(a_request(:get, url)).to have_been_made + end + end +end diff --git a/spec/operations/commerce/media/video/upload_spec.rb b/spec/operations/commerce/media/video/upload_spec.rb new file mode 100644 index 0000000..98cd861 --- /dev/null +++ b/spec/operations/commerce/media/video/upload_spec.rb @@ -0,0 +1,27 @@ +RSpec.describe EbayAPI, ".commerce.media.video.upload" do + let(:client) { described_class.new(settings) } + let(:scope) { client.commerce.media.video } + let(:settings) { yaml_fixture_file("settings.valid.yml").merge(api_subdomain: "apim") } + let(:video_id) { "92626cdc1820ab8eac2356d1ffffe6d0" } + let(:url) do + "https://apim.ebay.com/commerce/media/v1_beta/video/#{video_id}/upload" + end + let(:file) { "file data" } + + before { stub_request(:post, url).to_return(response) } + + subject do + scope.upload(id: video_id, file: file) + end + + context "success" do + let(:response) do + open_fixture_file "commerce/media/video/upload/success" + end + + it "sends a request" do + subject + expect(a_request(:post, url)).to have_been_made + end + end +end