1+ # -*- coding: utf-8 -*-
2+
3+ '''
4+ Authored by: Michal Hernas
5+ Licensed under CDDL 1.0
6+ '''
7+
8+ import os
9+
10+ from ebaysdk import log
11+ from ebaysdk .connection import BaseConnection
12+ from ebaysdk .exception import RequestPaginationError , PaginationLimit
13+ from ebaysdk .config import Config
14+ from ebaysdk .utils import dict2xml
15+
16+ class Connection (BaseConnection ):
17+ """Connection class for the Inventory Management service
18+
19+ API documentation:
20+ http://developer.ebay.com/Devzone/store-pickup/InventoryManagement/index.html
21+
22+ Supported calls:
23+ AddInventory
24+ AddInventoryLocation
25+ DeleteInventory
26+ DeleteInventoryLocation
27+ (all others, see API docs)
28+
29+ Doctests:
30+ Create location first
31+ >>> f = Connection(config_file=os.environ.get('EBAY_YAML'), debug=False)
32+ >>> retval = f.execute('AddInventoryLocation', {
33+ ... 'Address1': u'Alexanderplatz 12',
34+ ... 'Address2': u'Gebaude 6',
35+ ... 'City': u'Berlin',
36+ ... 'Country': u'DE',
37+ ... 'PostalCode': u'13355',
38+ ... 'Latitude': u'37.374488',
39+ ... 'Longitude': u'-122.032876',
40+ ... 'LocationID': u'ebaysdk_test',
41+ ... 'LocationType': u'STORE',
42+ ... 'Phone': u'(408)408-4080',
43+ ... 'URL': u'http://store.com',
44+ ... 'UTCOffset': u'+02:00',
45+ ... 'Name': 'Test',
46+ ... 'Region': 'Berlin',
47+ ... 'PickupInstructions': 'Pick it up soon',
48+ ... 'Hours': [{'Day': {'DayOfWeek': 1, 'Interval': {'Open': '08:00:00', 'Close': '10:00:00'}}}]
49+ ... })
50+ >>> error = f.error()
51+ >>> if not f.error():
52+ ... print(f.response.reply.LocationID.lower())
53+ ebaysdk_test
54+
55+ And now add item it it
56+ >>> f = Connection(config_file=os.environ.get('EBAY_YAML'), debug=False)
57+ >>> retval = f.execute('AddInventory', {"SKU": "SKU_TEST", "Locations": {"Location": [
58+ ... {"Availability": "IN_STOCK", "LocationID": "ebaysdk_test", "Quantity": 10}
59+ ... ]}})
60+ >>> error = f.error()
61+ >>> if not f.error():
62+ ... print(f.response.reply.SKU.lower())
63+ sku_test
64+
65+
66+ Delete item from all locations
67+ >>> f = Connection(config_file=os.environ.get('EBAY_YAML'), debug=False)
68+ >>> retval = f.execute('DeleteInventory', {"SKU": "SKU_TEST", "Confirm": 'true'})
69+ >>> error = f.error()
70+ >>> if not f.error():
71+ ... print(f.response.reply.SKU.lower())
72+ sku_test
73+
74+
75+ Delete location
76+ >>> f = Connection(config_file=os.environ.get('EBAY_YAML'), debug=False)
77+ >>> retval = f.execute('DeleteInventoryLocation', {"LocationID": "ebaysdk_test"})
78+ >>> error = f.error()
79+ >>> if not f.error():
80+ ... print(f.response.reply.LocationID.lower())
81+ ebaysdk_test
82+
83+ """
84+
85+ def __init__ (self , ** kwargs ):
86+ """Finding class constructor.
87+
88+ Keyword arguments:
89+ domain -- API endpoint (default: svcs.ebay.com)
90+ config_file -- YAML defaults (default: ebay.yaml)
91+ debug -- debugging enabled (default: False)
92+ warnings -- warnings enabled (default: False)
93+ uri -- API endpoint uri (default: /services/search/FindingService/v1)
94+ token -- eBay application/user token
95+ version -- version number (default: 1.0.0)
96+ https -- execute of https (default: False)
97+ proxy_host -- proxy hostname
98+ proxy_port -- proxy port number
99+ timeout -- HTTP request timeout (default: 20)
100+ parallel -- ebaysdk parallel object
101+ response_encoding -- API encoding (default: XML)
102+ request_encoding -- API encoding (default: XML)
103+ """
104+
105+ super (Connection , self ).__init__ (method = 'POST' , ** kwargs )
106+
107+ self .config = Config (domain = kwargs .get ('domain' , 'api.ebay.com' ),
108+ connection_kwargs = kwargs ,
109+ config_file = kwargs .get ('config_file' , 'ebay.yaml' ))
110+
111+ # override yaml defaults with args sent to the constructor
112+ self .config .set ('domain' , kwargs .get ('domain' , 'api.ebay.com' ))
113+ self .config .set ('uri' , '/selling/inventory/v1' )
114+ self .config .set ('https' , True )
115+ self .config .set ('warnings' , True )
116+ self .config .set ('errors' , True )
117+ self .config .set ('siteid' , None )
118+ self .config .set ('response_encoding' , 'XML' )
119+ self .config .set ('request_encoding' , 'XML' )
120+ self .config .set ('proxy_host' , None )
121+ self .config .set ('proxy_port' , None )
122+ self .config .set ('token' , None )
123+ self .config .set ('iaf_token' , None )
124+ self .config .set ('appid' , None )
125+ self .config .set ('version' , '1.0.0' )
126+ self .config .set ('service' , 'InventoryManagement' )
127+ self .config .set ('doc_url' , 'http://developer.ebay.com/Devzone/store-pickup/InventoryManagement/index.html' )
128+
129+ self .datetime_nodes = ['starttimefrom' , 'timestamp' , 'starttime' ,
130+ 'endtime' ]
131+ self .base_list_nodes = [
132+ ]
133+
134+ endpoints = {
135+ 'addinventorylocation' : 'locations/delta/add' ,
136+ 'addinventory' : 'inventory/delta/add' ,
137+ 'deleteinventory' : 'inventory/delta/delete' ,
138+ 'deleteinventorylocation' : 'locations/delta/delete' ,
139+ }
140+
141+ def build_request_url (self , verb ):
142+ url = super (Connection , self ).build_request_url (verb )
143+ endpoint = self .endpoints [verb .lower ()]
144+ return "{0}/{1}" .format (url , endpoint )
145+
146+ def build_request_headers (self , verb ):
147+ return {
148+ "Authorization" : "TOKEN {0}" .format (self .config .get ('token' )),
149+ "Content-Type" : "application/xml"
150+ }
151+
152+ def build_request_data (self , verb , data , verb_attrs ):
153+ xml = "<?xml version=\" 1.0\" encoding=\" utf-8\" ?>"
154+ xml += "<" + verb + "Request>"
155+ xml += dict2xml (data )
156+ xml += "</" + verb + "Request>"
157+
158+ return xml
159+
160+ def warnings (self ):
161+ warning_string = ""
162+
163+ if len (self ._resp_body_warnings ) > 0 :
164+ warning_string = "%s: %s" \
165+ % (self .verb , ", " .join (self ._resp_body_warnings ))
166+
167+ return warning_string
168+
169+
170+ def _get_resp_body_errors (self ):
171+ """Parses the response content to pull errors.
172+
173+ Child classes should override this method based on what the errors in the
174+ XML response body look like. They can choose to look at the 'ack',
175+ 'Errors', 'errorMessage' or whatever other fields the service returns.
176+ the implementation below is the original code that was part of error()
177+ """
178+
179+ if self ._resp_body_errors and len (self ._resp_body_errors ) > 0 :
180+ return self ._resp_body_errors
181+
182+ errors = []
183+ warnings = []
184+ resp_codes = []
185+
186+ if self .verb is None :
187+ return errors
188+
189+ dom = self .response .dom ()
190+ if dom is None :
191+ return errors
192+
193+ for e in dom .findall ('Errors' ):
194+ eSeverity = None
195+ eClass = None
196+ eShortMsg = None
197+ eLongMsg = None
198+ eCode = None
199+
200+ try :
201+ eSeverity = e .findall ('SeverityCode' )[0 ].text
202+ except IndexError :
203+ pass
204+
205+ try :
206+ eClass = e .findall ('ErrorClassification' )[0 ].text
207+ except IndexError :
208+ pass
209+
210+ try :
211+ eCode = e .findall ('ErrorCode' )[0 ].text
212+ except IndexError :
213+ pass
214+
215+ try :
216+ eShortMsg = e .findall ('ShortMessage' )[0 ].text
217+ except IndexError :
218+ pass
219+
220+ try :
221+ eLongMsg = e .findall ('LongMessage' )[0 ].text
222+ except IndexError :
223+ pass
224+
225+ try :
226+ eCode = e .findall ('ErrorCode' )[0 ].text
227+ try :
228+ int_code = int (eCode )
229+ except ValueError :
230+ int_code = None
231+
232+ if int_code and int_code not in resp_codes :
233+ resp_codes .append (int_code )
234+
235+ except IndexError :
236+ pass
237+
238+ msg = "Class: %s, Severity: %s, Code: %s, %s%s" \
239+ % (eClass , eSeverity , eCode , eShortMsg , eLongMsg )
240+
241+ if eSeverity == 'Warning' :
242+ warnings .append (msg )
243+ else :
244+ errors .append (msg )
245+
246+ self ._resp_body_warnings = warnings
247+ self ._resp_body_errors = errors
248+ self ._resp_codes = resp_codes
249+
250+ if self .config .get ('warnings' ) and len (warnings ) > 0 :
251+ log .warn ("%s: %s\n \n " % (self .verb , "\n " .join (warnings )))
252+
253+ if self .response .reply .Ack == 'Failure' :
254+ if self .config .get ('errors' ):
255+ log .error ("%s: %s\n \n " % (self .verb , "\n " .join (errors )))
256+
257+ return errors
258+
259+ return []
0 commit comments