mercado-libre 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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: []