mercado-libre 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 363f6161a791d57042f5398e81818cd213f77f1b
4
+ data.tar.gz: 61ba177f45aac40b736396aaedd46da57460b748
5
+ SHA512:
6
+ metadata.gz: 92f080efeed17ad6e8e6f0aa0a21d1885bb43a77e635e38f3c08195cdbb8c3ac2dbf0d30d68db35e108a047260cc72bbfc9a826fd8d5139ce7c45d9d71f04579
7
+ data.tar.gz: 734b70b76553754d2cb56bc78d4cd37b9570b042c293715b7143950453d33aa439f9f47c0b14d73069fc781856e38ad4899361f2f57b92df03e971ee55712e84
data/.gems ADDED
@@ -0,0 +1,3 @@
1
+ cutest -v 1.2.3
2
+ mock-server -v 0.1.2
3
+ requests -v 1.0.0
@@ -0,0 +1,2 @@
1
+ .gs
2
+ env.sh
@@ -0,0 +1,6 @@
1
+ TESTS=$(shell ls test/*.rb)
2
+
3
+ test:
4
+ ruby -W1 .gs/bin/cutest $(TESTS)
5
+
6
+ .PHONY: test
@@ -0,0 +1,3 @@
1
+ ML_TEST_APP_KEY=1234567890123456
2
+ ML_TEST_APP_SECRET=asdfghjklqwertyuiopzxcvvbnmaaaaa
3
+ # TESTING_AGAINST_ML_SERVER=yes
@@ -0,0 +1,3 @@
1
+ require_relative 'mercado_libre/version'
2
+ require_relative 'mercado_libre/item'
3
+ require_relative 'mercado_libre/api'
@@ -0,0 +1,94 @@
1
+ module MercadoLibre
2
+ require 'requests'
3
+ require "requests/sugar"
4
+ class API
5
+ attr_accessor :access_token
6
+
7
+ def initialize(params)
8
+ @app_key = params.fetch(:app_key)
9
+ @app_secret = params.fetch(:app_secret)
10
+ @host = params.fetch(:host) { ENV.fetch('MERCADOLIBRE_API_HOST') }
11
+ end
12
+
13
+ def publish_item(item)
14
+ url = "/items"
15
+ response = authenticated_request(:post, url, item.publishable_hash.to_json)
16
+
17
+ Item.new(response)
18
+ end
19
+
20
+ def update_item(item_id, params)
21
+ url = "/items/#{item_id}"
22
+ response = authenticated_request(:put, url, params.to_json)
23
+
24
+ Item.new(response)
25
+ end
26
+
27
+ def close_item(item_id)
28
+ url = "/items/#{item_id}"
29
+ authenticated_request(:put, url, {status: :closed}.to_json)
30
+ end
31
+
32
+ def relist_item(item_id, params)
33
+ url = "/items/#{item_id}/relist"
34
+ payload = {
35
+ price: params.fetch(:price),
36
+ quantity: params.fetch(:quantity),
37
+ listing_type_id: params.fetch(:listing_type_id)
38
+ }
39
+ response = authenticated_request(:post, url, payload.to_json)
40
+
41
+ Item.new response
42
+ end
43
+
44
+ def get_item(item_id)
45
+ url = "/items/#{item_id}"
46
+ response = authenticated_request(:get, url)
47
+
48
+ Item.new(response)
49
+ end
50
+
51
+ def authenticated_request(verb, url, data = {})
52
+ url_with_token = "#{url}?access_token=#{access_token}"
53
+ begin
54
+ request(verb, url_with_token, data)
55
+ rescue Requests::Error
56
+ @retries ||=0
57
+ @retries +=1
58
+ if $!.message.strip == 'Forbidden'
59
+ @access_token = nil
60
+ authenticated_request(verb, url, data) if @retries < 10
61
+ else
62
+ raise $!
63
+ end
64
+ end
65
+ end
66
+
67
+ def request(verb, url, data)
68
+ url = "#{@host}#{url}"
69
+ verb = verb.to_s.upcase
70
+ begin
71
+ response = Requests.request(verb, url, data: data)
72
+ rescue StandardError => e
73
+ new_message = [e.message.strip,e.response.body].join("\n")
74
+ raise $!, new_message, $!.backtrace
75
+ end
76
+ JSON.parse(response.body)
77
+ end
78
+
79
+ def access_token
80
+ @access_token ||= request_access_token
81
+ end
82
+
83
+ def request_access_token
84
+ url = "/oauth/token"
85
+ data = {
86
+ client_id: @app_key,
87
+ client_secret: @app_secret,
88
+ grant_type: 'client_credentials'
89
+ }
90
+
91
+ request(:post, url, data).fetch('access_token')
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,33 @@
1
+ module MercadoLibre
2
+ class Item
3
+ def self.publishable_params
4
+ [:site_id, :title, :category_id, :price, :currency_id, :available_quantity,
5
+ :buying_mode, :listing_type_id, :condition, :description, :video_id, :warranty,
6
+ :pictures, :shipping]
7
+ end
8
+
9
+ def self.attr_list
10
+ other_fields = [:id, :start_time, :stop_time, :end_time, :permalink, :thumbnail,
11
+ :secure_thumbnail, :descriptions, :accepts_mercadopago, :status, :date_created,
12
+ :last_updated, :non_mercado_pago_payment_methods ]
13
+
14
+ self.publishable_params + other_fields
15
+ end
16
+
17
+ attr_accessor *attr_list
18
+
19
+ def initialize(params = {})
20
+ params = Hash[params.map { |k, v| [k.to_sym, v] }] #Symbolize keys
21
+ self.class.attr_list.each do |k|
22
+ send("#{k}=", params[k]) if params[k]
23
+ end
24
+ end
25
+
26
+ def publishable_hash
27
+ hash = {}
28
+ self.class.publishable_params.each { |k| hash[k] = send(k) }
29
+
30
+ hash
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,3 @@
1
+ module MercadoLibre
2
+ VERSION = '0.0.2'
3
+ end
@@ -0,0 +1,16 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "mercado-libre"
3
+ s.version = "0.0.2"
4
+ s.summary = "MercadoLibre API"
5
+ s.description = "mercado-libre"
6
+ s.authors = ["CarlosIPe"]
7
+ s.email = ["carlos2@compendium.com.ar"]
8
+ s.homepage = "https://github.com/carlosipe/mercado-libre"
9
+ s.license = "MIT"
10
+
11
+ s.files = `git ls-files`.split("\n")
12
+
13
+ s.add_dependency "requests", '~> 1'
14
+ s.add_development_dependency "cutest", '~> 1'
15
+ s.add_development_dependency "mock-server", '~> 0'
16
+ end
@@ -0,0 +1,63 @@
1
+ $:.unshift File.expand_path("../../lib", __FILE__)
2
+ require 'mercado-libre'
3
+ include MercadoLibre
4
+
5
+ def assignable_properties
6
+ {
7
+ site_id: 'MLA',
8
+ title: 'title',
9
+ category_id: 'MLA1212',
10
+ price: 100.19,
11
+ currency_id: 'ARS',
12
+ available_quantity: 10,
13
+ buying_mode: 'buy_it_now',
14
+ listing_type_id: 'gold_special',
15
+ condition: 'new',
16
+ description: 'description eheh',
17
+ warranty: 'GARANTIA TOTAL CONTRA FALLAS',
18
+ video_id: nil,
19
+ pictures: [],
20
+ shipping: {mode: 'me2', local_pick_up: true},
21
+ start_time: "2015-12-30T20:28:49.097Z",
22
+ stop_time: "2016-02-28T20:28:49.097Z",
23
+ end_time: "2016-02-28T20:28:49.097Z",
24
+ permalink: "http://articulo.mercadolibre.com.ar/MLA-597837524-title-_JM",
25
+ thumbnail: "",
26
+ secure_thumbnail: "",
27
+ descriptions:[{id: "MLA597837524-998844341"}],
28
+ accepts_mercadopago: true,
29
+ non_mercado_pago_payment_methods:[],
30
+ status: "not_yet_active",
31
+ date_created: "2015-12-30T20:28:49.292Z",
32
+ last_updated: "2015-12-30T20:28:49.292Z"
33
+ }
34
+ end
35
+
36
+ def publishable_fields
37
+ {
38
+ site_id: 'MLA',
39
+ title: 'title',
40
+ category_id: 'MLA1212',
41
+ price: 100.19,
42
+ currency_id: 'ARS',
43
+ available_quantity: 10,
44
+ buying_mode: 'buy_it_now',
45
+ listing_type_id: 'gold_special',
46
+ condition: 'new',
47
+ description: 'description eheh',
48
+ warranty: 'GARANTIA TOTAL CONTRA FALLAS',
49
+ video_id: nil,
50
+ pictures: [],
51
+ shipping: {mode: 'me2', local_pick_up: true}
52
+ }
53
+ end
54
+
55
+ test "item sets all assignable properties" do
56
+ item = Item.new(assignable_properties)
57
+ assignable_properties.each{|k,v| assert_equal(item.send(k),v)}
58
+ end
59
+
60
+ test "item publishable_hash" do
61
+ item = Item.new(publishable_fields)
62
+ assert_equal(item.publishable_hash, publishable_fields)
63
+ end
@@ -0,0 +1,105 @@
1
+ $:.unshift File.expand_path("../../lib", __FILE__)
2
+ require 'mercado-libre'
3
+ require './test/mercadolibre_mock_server'
4
+ include MercadoLibre
5
+ mercadolibre_mock = MercadoLibreMock.new
6
+ mercadolibre_mock.run
7
+
8
+ File.read("env.sh").scan(/(.*?)="?(.*)"?$/).each do |key, value|
9
+ ENV[key] ||= value
10
+ end
11
+
12
+ def api
13
+ API.new(
14
+ app_key: ENV['ML_TEST_APP_KEY'],
15
+ app_secret: ENV['ML_TEST_APP_SECRET'],
16
+ host: ENV['TESTING_AGAINST_ML_SERVER'] ? 'https://api.mercadolibre.com' : 'http://localhost:4000'
17
+ )
18
+ end
19
+
20
+ def item
21
+ Item.new(
22
+ site_id: 'MLA',
23
+ title: 'title',
24
+ category_id: 'MLA1227',
25
+ price: 100.19,
26
+ currency_id: 'ARS',
27
+ available_quantity: 10,
28
+ buying_mode: 'buy_it_now',
29
+ listing_type_id: 'bronze',
30
+ condition: 'new',
31
+ description: 'description eheh',
32
+ video_id: nil,
33
+ warranty: 'GARANTIA TOTAL CONTRA FALLAS',
34
+ pictures: [],
35
+ shipping: {"mode"=> 'me2', "local_pick_up" => true}
36
+ )
37
+ end
38
+
39
+ test "API gets access token" do
40
+ assert( api.request_access_token() =~ /^APP/ )
41
+ end
42
+
43
+ test "API.publish item returns item with id and title" do
44
+ published_item = api.publish_item(item)
45
+ assert(published_item.id =~ /^MLA(\d*)/ )
46
+ assert_equal(published_item.title, 'Title')
47
+ end
48
+
49
+ test "publish item and check its published" do
50
+ published_item = api.publish_item(item)
51
+ assert_equal(api.get_item(published_item.id).title, 'Title')
52
+ end
53
+
54
+ test "updates item and check its updated" do
55
+ published_item = api.publish_item(item)
56
+ params = {title: 'New title', price: 200, available_quantity: 99}
57
+ api.update_item(published_item.id, params)
58
+ retrieved_item = api.get_item(published_item.id)
59
+
60
+ assert_equal(retrieved_item.title, 'New Title')
61
+ assert_equal(retrieved_item.price, 200)
62
+ assert_equal(retrieved_item.available_quantity, 99)
63
+ assert_equal(retrieved_item.id, published_item.id)
64
+ end
65
+
66
+ test "publish item returns item with permalink" do
67
+ published_item = api.publish_item(item)
68
+ assert(published_item.permalink =~ /http/)
69
+ end
70
+
71
+ test "closes item and status is closed" do
72
+ published_item = api.publish_item(item)
73
+ sleep(10) if ENV['TESTING_AGAINST_ML_SERVER']
74
+ api.close_item(published_item.id)
75
+ assert_equal(api.get_item(published_item.id).status, 'closed')
76
+ end
77
+
78
+ test "relists an item returns item with new ML_ID and values" do
79
+ published_item = api.publish_item(item)
80
+ sleep(10) if ENV['TESTING_AGAINST_ML_SERVER']
81
+ api.close_item(published_item.id)
82
+ relisted_item = api.relist_item(published_item.id,{
83
+ price: 90,
84
+ quantity: 40,
85
+ listing_type_id: 'bronze'
86
+ })
87
+ assert_equal(relisted_item.price, 90)
88
+ assert_equal(relisted_item.available_quantity, 40)
89
+ end
90
+
91
+ test "returns verbose exceptions" do
92
+ begin
93
+ api.request(:post, '/oauth/token', {client_id: '123'})
94
+ rescue StandardError =>e
95
+ verbose_error_message = "Bad Request\n{\"message\":\"grant_type is a required parameter\",\"error\":\"invalid_request\",\"status\":400,\"cause\":[]}"
96
+ assert_equal(e.message, verbose_error_message )
97
+ end
98
+ end
99
+
100
+ test "authenticated_request gets new access token when forbidden" do
101
+ ml = api
102
+ ml.access_token = 'Invalid one'
103
+ published_item = ml.publish_item(item)
104
+ assert(ml.access_token =~ /^APP/)
105
+ end
@@ -0,0 +1,62 @@
1
+ require 'mock_server'
2
+ class MercadoLibreMock
3
+ include MockServer::Methods
4
+ PRODUCTS = {}
5
+
6
+ def run
7
+ mock_server {
8
+ post "/oauth/token" do
9
+ if !params['grant_type']
10
+ status 400
11
+ content_type :json
12
+ '{"message":"grant_type is a required parameter","error":"invalid_request","status":400,"cause":[]}'
13
+ else
14
+ content_type :json
15
+ { access_token: 'APP12343' }.to_json
16
+ end
17
+ end
18
+
19
+ post '/items' do
20
+ halt 403 unless params[:access_token] == 'APP12343'
21
+ data = JSON.parse(request.body.read)
22
+ data['id'] = 'MLA1234'
23
+ data['title'] = data['title'].split.map(&:capitalize)*' '
24
+ data['permalink'] = 'http://mercadolibre.com/MLA/item/MLA1234'
25
+ PRODUCTS["MLA1234"] = data
26
+
27
+ content_type :json
28
+ PRODUCTS.fetch('MLA1234').to_json
29
+ end
30
+
31
+ get '/items/:id' do |id|
32
+ content_type :json
33
+ PRODUCTS.fetch(id).to_json { raise Sinatra::NotFound }
34
+ end
35
+
36
+ put '/items/:id' do |id|
37
+ halt 403 unless params[:access_token] == 'APP12343'
38
+ product = PRODUCTS.fetch(id) { raise Sinatra::NotFound }
39
+ data = JSON.parse(request.body.read)
40
+ PRODUCTS[id] = product.merge(data)
41
+ PRODUCTS[id]['title'] = PRODUCTS[id]['title'].split.map(&:capitalize)*' '
42
+
43
+ content_type :json
44
+ PRODUCTS[id].to_json
45
+ end
46
+
47
+ post '/items/:id/relist' do |id|
48
+ halt 403 unless params[:access_token] == 'APP12343'
49
+ product = PRODUCTS.fetch(id) { raise Sinatra::NotFound }
50
+ data = JSON.parse(request.body.read)
51
+ product['id'] = 'MLA4321'
52
+ product['price'] = data.fetch('price')
53
+ product['available_quantity'] = data.fetch('quantity')
54
+ product['listing_type_id'] = data.fetch 'listing_type_id'
55
+ PRODUCTS['MLA4321'] = product
56
+
57
+ content_type :json
58
+ PRODUCTS['MLA4321'].to_json
59
+ end
60
+ }
61
+ end
62
+ end
metadata ADDED
@@ -0,0 +1,98 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mercado-libre
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - CarlosIPe
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-01-21 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: requests
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1'
27
+ - !ruby/object:Gem::Dependency
28
+ name: cutest
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1'
41
+ - !ruby/object:Gem::Dependency
42
+ name: mock-server
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: mercado-libre
56
+ email:
57
+ - carlos2@compendium.com.ar
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - ".gems"
63
+ - ".gitignore"
64
+ - Makefile
65
+ - env.sh.example
66
+ - lib/mercado-libre.rb
67
+ - lib/mercado_libre/api.rb
68
+ - lib/mercado_libre/item.rb
69
+ - lib/mercado_libre/version.rb
70
+ - mercado-libre.gemspec
71
+ - test/item.rb
72
+ - test/mercadolibre.rb
73
+ - test/mercadolibre_mock_server.rb
74
+ homepage: https://github.com/carlosipe/mercado-libre
75
+ licenses:
76
+ - MIT
77
+ metadata: {}
78
+ post_install_message:
79
+ rdoc_options: []
80
+ require_paths:
81
+ - lib
82
+ required_ruby_version: !ruby/object:Gem::Requirement
83
+ requirements:
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ version: '0'
87
+ required_rubygems_version: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
92
+ requirements: []
93
+ rubyforge_project:
94
+ rubygems_version: 2.4.5.1
95
+ signing_key:
96
+ specification_version: 4
97
+ summary: MercadoLibre API
98
+ test_files: []