1# -*- coding: utf-8 -*- 2 3''' 4Copyright 2012-2019 eBay Inc. 5Authored by: Tim Keefer 6Licensed under CDDL 1.0 7''' 8 9from ebaysdk import log 10from ebaysdk.connection import BaseConnection 11from ebaysdk.config import Config 12from ebaysdk.utils import getNodeText, dict2xml 13 14 15class Connection(BaseConnection): 16 """Connection class for a base SOA service""" 17 18 def __init__(self, app_config=None, site_id='EBAY-US', debug=False, **kwargs): 19 """SOA Connection class constructor""" 20 21 super(Connection, self).__init__(method='POST', debug=debug, **kwargs) 22 23 self.config = Config(domain=kwargs.get('domain', ''), 24 connection_kwargs=kwargs, 25 config_file=kwargs.get('config_file', 'ebay.yaml')) 26 27 self.config.set('https', False) 28 self.config.set('site_id', site_id) 29 self.config.set('content_type', 'text/xml;charset=UTF-8') 30 self.config.set('request_encoding', 'XML') 31 self.config.set('response_encoding', 'XML') 32 self.config.set('message_protocol', 'SOAP12') 33 # http://www.ebay.com/marketplace/fundraising/v1/services', 34 self.config.set('soap_env_str', '') 35 36 ph = None 37 pp = 80 38 if app_config: 39 self.load_from_app_config(app_config) 40 ph = self.config.get('proxy_host', ph) 41 pp = self.config.get('proxy_port', pp) 42 43 # override this method, to provide setup through a config object, which 44 # should provide a get() method for extracting constants we care about 45 # this method should then set the .api_config[] dict (e.g. the comment 46 # below) 47 def load_from_app_config(self, app_config): 48 # self.api_config['domain'] = app_config.get('API_SERVICE_DOMAIN') 49 # self.api_config['uri'] = app_config.get('API_SERVICE_URI') 50 pass 51 52 # Note: this method will always return at least an empty object_dict! 53 # It used to return None in some cases. If you get an empty dict, 54 # you can use the .error() method to look for the cause. 55 def response_dict(self): 56 return self.response.dict() 57 58 def build_request_headers(self, verb): 59 return { 60 'Content-Type': self.config.get('content_type'), 61 'X-EBAY-SOA-SERVICE-NAME': self.config.get('service'), 62 'X-EBAY-SOA-OPERATION-NAME': verb, 63 'X-EBAY-SOA-GLOBAL-ID': self.config.get('site_id'), 64 'X-EBAY-SOA-REQUEST-DATA-FORMAT': self.config.get('request_encoding'), 65 'X-EBAY-SOA-RESPONSE-DATA-FORMAT': self.config.get('response_encoding'), 66 'X-EBAY-SOA-MESSAGE-PROTOCOL': self.config.get('message_protocol'), 67 } 68 69 def build_request_data(self, verb, data, verb_attrs): 70 xml = '<?xml version="1.0" encoding="utf-8"?>' 71 xml += '<soap:Envelope' 72 xml += ' xmlns:soap="http://www.w3.org/2003/05/soap-envelope"' 73 xml += ' xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"' 74 xml += ' xmlns:ser="%s" >' % self.config.get('soap_env_str') 75 xml += '<soap:Body>' 76 xml += '<ser:%sRequest>' % verb 77 xml += dict2xml(self.soapify(data), self.escape_xml) 78 xml += '</ser:%sRequest>' % verb 79 xml += '</soap:Body>' 80 xml += '</soap:Envelope>' 81 return xml 82 83 def soapify(self, xml): 84 xml_type = type(xml) 85 if xml_type == dict: 86 soap = {} 87 for k, v in list(xml.items()): 88 if k == '@attrs' or k == '#text': 89 soap[k] = v 90 91 # skip nodes that have ns defined 92 elif ':' in k: 93 soap[k] = self.soapify(v) 94 else: 95 soap['ser:%s' % (k)] = self.soapify(v) 96 97 elif xml_type == list: 98 soap = [] 99 for x in xml: 100 soap.append(self.soapify(x)) 101 else: 102 soap = xml 103 return soap 104 105 def warnings(self): 106 warning_string = "" 107 108 if len(self._resp_body_warnings) > 0: 109 warning_string = "%s: %s" \ 110 % (self.verb, ", ".join(self._resp_body_warnings)) 111 112 return warning_string 113 114 def _get_resp_body_errors(self): 115 """Parses the response content to pull errors. 116 117 Child classes should override this method based on what the errors in the 118 XML response body look like. They can choose to look at the 'ack', 119 'Errors', 'errorMessage' or whatever other fields the service returns. 120 the implementation below is the original code that was part of error() 121 122 <errorMessage xmlns="http://www.ebay.com/marketplace/search/v1/services"><error><errorId>5014</errorId><domain>CoreRuntime</domain><severity>Error</severity><category>System</category><message> 123 """ 124 125 if self._resp_body_errors and len(self._resp_body_errors) > 0: 126 return self._resp_body_errors 127 128 errors = [] 129 warnings = [] 130 resp_codes = [] 131 132 if self.verb is None: 133 return errors 134 135 dom = self.response.dom() 136 if dom is None: 137 return errors 138 139 for e in dom.findall('error'): 140 141 eSeverity = None 142 eDomain = None 143 eMsg = None 144 eId = None 145 146 try: 147 eSeverity = e.findall('severity')[0].text 148 except IndexError: 149 pass 150 151 try: 152 eDomain = e.findall('domain')[0].text 153 except IndexError: 154 pass 155 156 try: 157 eId = e.findall('errorId')[0].text 158 if int(eId) not in resp_codes: 159 resp_codes.append(int(eId)) 160 except IndexError: 161 pass 162 163 try: 164 eMsg = e.findall('message')[0].text 165 except IndexError: 166 pass 167 168 msg = "Domain: %s, Severity: %s, errorId: %s, %s" \ 169 % (eDomain, eSeverity, eId, eMsg) 170 171 if eSeverity == 'Warning': 172 warnings.append(msg) 173 else: 174 errors.append(msg) 175 176 self._resp_body_warnings = warnings 177 self._resp_body_errors = errors 178 self._resp_codes = resp_codes 179 180 if self.config.get('warnings') and len(warnings) > 0: 181 log.warn("%s: %s\n\n" % (self.verb, "\n".join(warnings))) 182 183 try: 184 if self.response.reply.ack == 'Success' and len(errors) > 0 and self.config.get('errors'): 185 log.error("%s: %s\n\n" % (self.verb, "\n".join(errors))) 186 elif len(errors) > 0: 187 if self.config.get('errors'): 188 log.error("%s: %s\n\n" % (self.verb, "\n".join(errors))) 189 return errors 190 except AttributeError: 191 pass 192 193 return [] 194