def get_termination_by_employee_identifier(client, employee_identifier): zeep_client = Zeep("{0}{1}".format(client.base_url, endpoint)) if 'EmployeeNumber' in employee_identifier: element = zeep_client.get_element('ns6:EmployeeNumberIdentifier') obj = element(**employee_identifier) elif 'EmailAddress' in employee_identifier: element = zeep_client.get_element('ns6:EmailAddressIdentifier') obj = element(**employee_identifier) response = zeep_client.service.GetTerminationByEmployeeIdentifier( _soapheaders=[client.session_header], employeeIdentifier=obj) return response['Results']
def get_person_by_employee_identifier(client, employee_identifier): zeep_client = Zeep(f"{client.base_url}{endpoint}") if 'EmployeeNumber' in employee_identifier: element = zeep_client.get_element('ns6:EmployeeNumberIdentifier') obj = element(**employee_identifier) elif 'EmailAddress' in employee_identifier: element = zeep_client.get_element('ns6:EmailAddressIdentifier') obj = element(**employee_identifier) response = zeep_client.service.GetPersonByEmployeeIdentifier( _soapheaders=[client.session_header], employeeIdentifier=obj) return response['Results']
def _call_federation(settings: CzNiaAppSettings, transport: Transport, assertion: Element) -> Element: """Call FPSTS (Federation provider) service and return the assertion.""" plugins = [] history = None if settings.DEBUG: history = HistoryPlugin() plugins.append(history) client = Client(settings.FEDERATION_WSDL, wsse=SAMLTokenSignature(assertion), settings=SETTINGS, transport=transport, plugins=plugins) # prepare request request_type = client.get_element( QName(NiaNamespaces.WS_TRUST.value, 'RequestType')) request = AnyObject(request_type, request_type(NiaNamespaces.WS_TRUST.value + '/Issue')) # Prepare WSA header applies = _get_wsa_header(client, settings.PUBLIC_ADDRESS) # Call the service service = client.bind('SecurityTokenService', 'WS2007FederationHttpBinding_IWSTrust13Sync') try: response = service.Trust13Issue(_value_1=[applies, request]) except (Error, XmlsecError, RequestException) as err: _log_history(history, settings, 'FPSTS', success=False) raise NiaException(err) _log_history(history, settings, 'FPSTS') return response.RequestSecurityTokenResponse[0]['_value_1'][3]['_value_1']
class TransmissaoSOAP(Transmissao): def __init__(self, certificado, session=Session(), cache=True, disable_warnings=True, raw_response=True): """ :param certificado: erpbrasil.assinatura.certificado :param cache: O cache torna as requisições mais rápidas entretanto, pode causar problemas em caso de troca de parametros dos webservices """ if cache: self._cache = self.get_cache() self.certificado = certificado self.session = session self._disable_warnings = disable_warnings self.raw_response = raw_response @staticmethod def get_cache(): temp_dir = tempfile.gettempdir() cache_file = os.path.join(temp_dir, 'erpbrasil_transmissao.db') return SqliteCache(path=cache_file, timeout=60) def desativar_avisos(self): if self._disable_warnings: requests.packages.urllib3.disable_warnings( InsecureRequestWarning ) @contextmanager def cliente(self, url, verify=False, service_name=None, port_name=None): with ArquivoCertificado(self.certificado, 'w') as (key, cert): self.desativar_avisos() session = Session() session.cert = (key, cert) session.verify = verify transport = Transport(session=session, cache=self._cache) self._cliente = Client( url, transport=transport, service_name=service_name, port_name=port_name ) yield self._cliente self._cliente = False def interpretar_mensagem(self, mensagem): if type(mensagem) == str: return etree.fromstring(mensagem, parser=etree.XMLParser( remove_blank_text=True )) return mensagem def set_header(self, elemento, **kwargs): header_element = self._cliente.get_element(elemento) header = header_element(**kwargs) self._cliente.set_default_soapheaders([header]) def enviar(self, operacao, mensagem): with self._cliente.settings(raw_response=self.raw_response): return self._cliente.service[operacao]( self.interpretar_mensagem(mensagem) )
def test_parse_soap_wsdl(): client = Client( 'tests/wsdl_files/soap.wsdl', transport=Transport(), ) response = """ <?xml version="1.0"?> <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:stoc="http://example.com/stockquote.xsd"> <soapenv:Header/> <soapenv:Body> <stoc:TradePrice> <price>120.123</price> </stoc:TradePrice> </soapenv:Body> </soapenv:Envelope> """.strip() client.set_ns_prefix('stoc', 'http://example.com/stockquote.xsd') with requests_mock.mock() as m: m.post('http://example.com/stockquote', text=response) account_type = client.get_type('stoc:account') account = account_type(id=100) account.user = '******' country = client.get_element('stoc:country').type() country.name = 'The Netherlands' country.code = 'NL' result = client.service.GetLastTradePrice(tickerSymbol='foobar', account=account, country=country) assert result == 120.123 request = m.request_history[0] # Compare request body expected = """ <soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:stoc="http://example.com/stockquote.xsd"> <soap-env:Body> <stoc:TradePriceRequest> <tickerSymbol>foobar</tickerSymbol> <account> <id>100</id> <user>mvantellingen</user> </account> <stoc:country> <name>The Netherlands</name> <code>NL</code> </stoc:country> </stoc:TradePriceRequest> </soap-env:Body> </soap-env:Envelope> """ assert_nodes_equal(expected, request.body)
def test_parse_soap_wsdl(): client = Client('tests/wsdl_files/soap.wsdl', transport=Transport(),) response = """ <?xml version="1.0"?> <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:stoc="http://example.com/stockquote.xsd"> <soapenv:Header/> <soapenv:Body> <stoc:TradePrice> <price>120.123</price> </stoc:TradePrice> </soapenv:Body> </soapenv:Envelope> """.strip() client.set_ns_prefix('stoc', 'http://example.com/stockquote.xsd') with requests_mock.mock() as m: m.post('http://example.com/stockquote', text=response) account_type = client.get_type('stoc:account') account = account_type(id=100) account.user = '******' country = client.get_element('stoc:country').type() country.name = 'The Netherlands' country.code = 'NL' result = client.service.GetLastTradePrice( tickerSymbol='foobar', account=account, country=country) assert result == 120.123 request = m.request_history[0] # Compare request body expected = """ <soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:stoc="http://example.com/stockquote.xsd"> <soap-env:Body> <stoc:TradePriceRequest> <tickerSymbol>foobar</tickerSymbol> <account> <id>100</id> <user>mvantellingen</user> </account> <stoc:country> <name>The Netherlands</name> <code>NL</code> </stoc:country> </stoc:TradePriceRequest> </soap-env:Body> </soap-env:Envelope> """ assert_nodes_equal(expected, request.body)
def execute_report(client, context, report_path, delimiter=','): session = requests.Session() session.headers.update({'US-DELIMITER': delimiter}) transport = Transport(session=session) payload = {'ReportPath': report_path} zeep_client = ZeepClient(f"{client.base_url}{endpoint}", transport=transport) element = zeep_client.get_element('ns5:ReportRequest') obj = element(**payload) r = zeep_client.service.ExecuteReport(request=obj, context=context) return r['ReportKey']
def _call_identity(settings: CzNiaAppSettings, transport: Transport) -> Element: """Call IPSTS (Identity provider) service and return the assertion.""" plugins = [] history = None if settings.DEBUG: history = HistoryPlugin() plugins.append(history) client = Client(settings.IDENTITY_WSDL, wsse=BinarySignature(settings.KEY, settings.CERTIFICATE, settings.PASSWORD), settings=SETTINGS, transport=transport, plugins=plugins) # Prepare token token_type = client.get_element( QName(NiaNamespaces.WS_TRUST.value, 'TokenType')) token = AnyObject(token_type, token_type(ASSERTION)) # Prepare request request_type = client.get_element( QName(NiaNamespaces.WS_TRUST.value, 'RequestType')) request = AnyObject(request_type, request_type(NiaNamespaces.WS_TRUST.value + '/Issue')) # Prepare key key_type = client.get_element( QName(NiaNamespaces.WS_TRUST.value, 'KeyType')) key = AnyObject(key_type, key_type(NiaNamespaces.WS_TRUST.value + '/SymmetricKey')) # Prepare WSA header applies = _get_wsa_header(client, settings.FEDERATION_ADDRESS) # Call the service service = client.bind('SecurityTokenService', 'WS2007HttpBinding_IWSTrust13Sync2') try: response = service.Trust13Issue( _value_1=[token, request, key, applies]) except (Error, XmlsecError, RequestException) as err: _log_history(history, settings, 'IPSTS', success=False) raise NiaException(err) _log_history(history, settings, 'IPSTS') return response.RequestSecurityTokenResponse[0]['_value_1'][3]['_value_1']
def log_on_with_token(client): # print(inspect.getmembers(client)) credentials = { 'Token': client.token, 'ClientAccessKey': client.client_access_key } # Log on to get ns5:DataContext object with auth zeep_client = ZeepClient(f"{client.base_url}{endpoint}") element = zeep_client.get_element('ns5:LogOnWithTokenRequest') obj = element(**credentials) # print(inspect.getmembers(obj)) return zeep_client.service.LogOnWithToken(obj)
def do_sync(): LOGGER.info("Starting sync") client = Client(AWIN_PUBLISHER_API) UserAuthentication = client.get_element( "{http://api.affiliatewindow.com/}UserAuthentication")( iId=CONFIG['publisher_id'], sPassword=CONFIG['api_password'], sType=CONFIG['user_type']) client.set_default_soapheaders([UserAuthentication]) sync_transactions(client) sync_advertisers(client) singer.write_state(STATE) LOGGER.info("Sync complete")
def build_zeep_client(self, alias='default'): config = self[alias] wsdl = config['wsdl'] proxies = config.get('proxies', None) session = Session() session.verify = False transport = Transport(session=session) client = Zeep_client(wsdl, transport=transport) if proxies: client.transport.session.proxies = proxies auth = client.get_element( '{http://datalinkservices.org/}Authorization') client.set_default_soapheaders([ auth( AcctID=config['account'], UserID=config['user'], Password=config['password'], ) ]) return client
def action_api_import_cron(self): #PREPROD #url_base = "http://213.60.254.109:85/Services/" #PROD url_base = "http://www.cegacol.com/Services/" url_api = url_base + "WsCatalogue.asmx?WSDL" client = Client(url_api) header = client.get_element("{" + url_base + "}AuthSoapHeader") end_date = datetime.strptime(fields.Date.today(), "%Y-%m-%d").\ date() month_ago = datetime(end_date.year, end_date.month - 1, end_date.day).date() milk_control_service = self.env.\ ref('res_partner_farm_data.service_control_lechero') passwds = self.env['res.partner.passwd'].search([ ('service', '=', milk_control_service.id) ]) for passwd in passwds: stop_before = False if not passwd.passwd or not passwd.name: continue header_value = header(UserName=passwd.name, Password=passwd.read(['passwd' ])[0]['passwd']) client.set_default_soapheaders([header_value]) last_sync_date = passwd.last_sync_date if last_sync_date: start_date = datetime.\ strptime(last_sync_date, "%Y-%m-%d").date() + \ timedelta(1) if start_date < month_ago: start_date = month_ago else: start_date = month_ago delta = end_date - start_date for i in range(delta.days + 1): vals = {} filter_date = start_date + timedelta(days=i) try: data = client.service.\ Controles( dateInterval={'DateFrom': filter_date, 'DateTo': filter_date}, idRangeQuery=0, status=0) except Exception, e: self.create({ 'date': filter_date.strftime("%Y-%m-%d"), 'exploitation_id': passwd.partner_id.id, 'state': 'incorrect', 'exception_txt': e.message }) break if not data['ListResult']: if data['MessageType']['Id'] != 30010: self.create({ 'date': filter_date.strftime("%Y-%m-%d"), 'exploitation_id': passwd.partner_id.id, 'state': 'incorrect', 'exception_txt': data['MessageType']['Description'] }) else: continue else: milk_control = self.\ create({'date': filter_date.strftime("%Y-%m-%d"), 'exploitation_id': passwd.partner_id.id, 'state': 'correct'}) for cow_data in data['ListResult']['Control']: if not cow_data['Grasa'] or cow_data[ 'Grasa'] == Decimal('0.00'): stop_before = True break vals = { 'control_id': milk_control.id, 'cea': cow_data['Cea'], 'cib': cow_data['Cib'], 'name': cow_data['Nombre'], 'date_birth': cow_data['FechaParto'].date().strftime("%Y-%m-%d"), 'birth_number': cow_data['NumeroParto'], 'control_number': cow_data['NumeroControl'], 'days': (cow_data['FechaControl'] - cow_data['FechaParto']).days, 'milk_liters': cow_data['LecheKilos'], 'fat': cow_data['Grasa'], 'protein': cow_data['Proteina'], 'rcs': int(cow_data['RCS']), 'urea': int(cow_data['Urea']) } vals['cumulative_milk'] = \ int(vals['milk_liters'] * vals['days']) self.env['milk.control.line'].create(vals) if stop_before: milk_control.unlink() passwd.last_sync_date = \ filter_date.strftime("%Y-%m-%d") break milk_control.create_report() passwd.last_sync_date = filter_date.strftime("%Y-%m-%d")
class XMLAConnection(object): @classmethod def addMethod(cls, funcname, func): return setattr(cls, funcname, func) @classmethod def setupMembers(cls): def getFunc(schemaName): return lambda this, *args, **kw: cls.Discover( this, schemaName, *args, **kw) for schemaName in xmla1_1_rowsets: mname = schemaNameToMethodName(schemaName) cls.addMethod(mname, getFunc(schemaName)) def __init__(self, url, location, sslverify, **kwargs): if "session" in kwargs: session = kwargs["session"] del kwargs["session"] transport = Transport(session=session) else: transport = Transport() if "auth" in kwargs: transport.session.auth = kwargs["auth"] del kwargs["auth"] transport.session.verify = sslverify self.sessionplugin = SessionPlugin(self) plugins = [self.sessionplugin] if "log" in kwargs: log = kwargs.get("log") if isinstance(log, Plugin): plugins.append(log) elif log == True: plugins.append(LogRequest()) del kwargs["log"] self.client = Client( url, transport=transport, # cache=None, unwrap=False, plugins=plugins) self.service = self.client.create_service( ns_name(schema_xmla, "MsXmlAnalysisSoap"), location) self.client.set_ns_prefix(None, schema_xmla) # optional, call might fail self.getMDSchemaLevels = lambda *args, **kw: self.Discover( "MDSCHEMA_LEVELS", *args, **kw) self.setListenOnSessionId(False) self.setSessionId(None) self._soapheaders = None def getListenOnSessionId(self): return self.listenOnSessionId def setListenOnSessionId(self, trueOrFalse): self.listenOnSessionId = trueOrFalse def setSessionId(self, sessionId): self.sessionId = sessionId def Discover(self, what, restrictions=None, properties=None): rl = as_etree(restrictions, "RestrictionList") pl = as_etree(properties, "PropertyList") try: #import pdb; pdb.set_trace() doc = self.service.Discover(RequestType=what, Restrictions=rl, Properties=pl, _soapheaders=self._soapheaders) root = fromETree(doc.body["return"]["_value_1"], ns=schema_xmla_rowset) res = getattr(root, "row", []) if res: res = aslist(res) except Fault as fault: raise XMLAException(fault.message, dictify(fromETree(fault.detail, ns=None))) #logger.debug( res ) return res def Execute(self, command, dimformat="Multidimensional", axisFormat="TupleFormat", **kwargs): if isinstance(command, stringtypes): command = as_etree({"Statement": command}) props = {"Format": dimformat, "AxisFormat": axisFormat} props.update(kwargs) plist = as_etree({"PropertyList": props}) try: res = self.service.Execute(Command=command, Properties=plist, _soapheaders=self._soapheaders) root = res.body["return"]["_value_1"] return TupleFormatReader(fromETree(root, ns=schema_xmla_mddataset)) except Fault as fault: raise XMLAException(fault.message, dictify(fromETree(fault.detail, ns=None))) def BeginSession(self): bs = self.client.get_element(ns_name(schema_xmla, "BeginSession"))(mustUnderstand=1) self.setListenOnSessionId(True) cmd = as_etree("Statement") self.service.Execute(Command=cmd, _soapheaders={"BeginSession": bs}) self.setListenOnSessionId(False) sess = self.client.get_element(ns_name(schema_xmla, "Session"))( SessionId=self.sessionId, mustUnderstand=1) self._soapheaders = {"Session": sess} def EndSession(self): if self.sessionId is not None: es = self.client.get_element(ns_name(schema_xmla, "EndSession"))( SessionId=self.sessionId, mustUnderstand=1) cmd = as_etree("Statement") self.service.Execute(Command=cmd, _soapheaders={"EndSession": es}) self.setSessionId(None) self._soapheaders = None
def _get_wsa_header(client: Client, address: str) -> AnyObject: """Get WSA header from the client.""" applies_type = client.get_element(QName(WSP, 'AppliesTo')) reference_type = client.get_element(QName(WSA, 'EndpointReference')) reference = AnyObject(reference_type, reference_type(Address=address)) return AnyObject(applies_type, applies_type(_value_1=reference))
class Adapter: xml_template = '<ds:Signature ' \ 'xmlns:ds="http://www.w3.org/2000/09/xmldsig#">' \ '<ds:SignedInfo>' \ '<ds:CanonicalizationMethod ' \ 'Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>' \ '<ds:SignatureMethod ' \ 'Algorithm="http://www.w3.org/2001/04/xmldsig-more#gostr34102001-gostr3411"/>' \ '<ds:Reference URI="#SIGNED_BY_CALLER"><ds:Transforms>' \ '<ds:Transform ' \ 'Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>' \ '<ds:Transform ' \ 'Algorithm="urn://smev-gov-ru/xmldsig/transform"/>' \ '</ds:Transforms>' \ '<ds:DigestMethod ' \ 'Algorithm="http://www.w3.org/2001/04/xmldsig-more#gostr3411"/>' \ '<ds:DigestValue></ds:DigestValue></ds:Reference>' \ '</ds:SignedInfo><ds:SignatureValue></ds:SignatureValue>' \ '<ds:KeyInfo><ds:X509Data><ds:X509Certificate>' \ '</ds:X509Certificate></ds:X509Data></ds:KeyInfo>' \ '</ds:Signature>' def __init__( self, wsdl="http://smev3-d.test.gosuslugi.ru:7500/smev/v1.2/ws?wsdl", ftp_addr="ftp://smev3-d.test.gosuslugi.ru/", history=False, method='sharp', serial=None, container=None, crt_name=None): self.log = logging.getLogger('smev.adapter') self.log.setLevel(logging.root.level) self.ftp_addr = ftp_addr self.crypto = Crypto() self.crypto.serial = serial self.crypto.crt_name = crt_name self.crypto.container = container self.method = method if history: self.history = HistoryPlugin() self.proxy = Client(wsdl, plugins=[self.history]) else: self.proxy = Client(wsdl) def dump(self): res = "Prefixes:\n" for prefix, namespace in self.proxy.wsdl.types.prefix_map.items(): res += ' ' * 4 + '%s: %s\n' % (prefix, namespace) res += "\nGlobal elements:\n" for elm_obj in sorted(self.proxy.wsdl.types.elements, key=lambda k: k.qname): value = elm_obj.signature(schema=self.proxy.wsdl.types) res += ' ' * 4 + value + '\n' res += "\nGlobal types:\n" for type_obj in sorted(self.proxy.wsdl.types.types, key=lambda k: k.qname or ''): value = type_obj.signature(schema=self.proxy.wsdl.types) res += ' ' * 4 + value + '\n' res += "\nBindings:\n" for binding_obj in sorted(self.proxy.wsdl.bindings.values(), key=lambda k: six.text_type(k)): res += ' ' * 4 + six.text_type(binding_obj) + '\n' res += '\n' for service in self.proxy.wsdl.services.values(): res += six.text_type(service) + '\n' for port in service.ports.values(): res += ' ' * 4 + six.text_type(port) + '\n' res += ' ' * 8 + 'Operations:\n' operations = sorted(port.binding._operations.values(), key=operator.attrgetter('name')) for operation in operations: res += '%s%s\n' % (' ' * 12, six.text_type(operation)) res += '\n' return res # def get_request(self, uri='urn://augo/smev/uslugi/1.0.0', # local_name='directum'): def get_request(self, uri=None, local_name=None, node_id=None, gen_xml_only=False): # if (uri and not local_name) or (not uri and local_name): # raise Exception( # 'uri и local_name необходимо указывать одновременно') operation = 'GetRequest' timestamp = datetime.now() node = self.proxy.create_message( self.proxy.service, operation, { 'NamespaceURI': uri, 'RootElementLocalName': local_name, 'Timestamp': timestamp, 'NodeID': node_id }, CallerInformationSystemSignature=etree.Element('Signature')) node[0][0][0].set('Id', 'SIGNED_BY_CALLER') node_str = etree.tostring(node) self.log.debug(node_str) res = self.__call_sign( self.__xml_part(node_str, b'ns1:MessageTypeSelector')) # COM variant # res = self.crypto.sign_com( # self.__xml_part(node_str, b'ns1:MessageTypeSelector').decode()) # res = self.__xml_part(res, 'Signature') # res = res.replace('URI=""', 'URI="#SIGNED_BY_CALLER"') # CSP variant # self.crypto.container = "049fc71a-1ff0-4e06-8714-03303ae34afd" # res = self.crypto.sign_csp( # self.__xml_part(node_str, b'ns1:MessageTypeSelector')) # Sharp variant # self.crypto.serial = '008E BDC8 291F 0003 81E7 11E1 AF7A 5ED3 27' # res = self.crypto.sign_sharp( # self.__xml_part(node_str, b'ns1:MessageTypeSelector')) res = node_str.decode().replace('<Signature/>', res) if gen_xml_only: return res res = self.__send(operation, res) self.log.debug(type(res)) self.log.debug(res) declar, uuid, reply_to, files = None, None, None, {} if 'MessagePrimaryContent' in res: xml = etree.fromstring(res) declar = Declar.parsexml( etree.tostring( xml.find('.//{urn://augo/smev/uslugi/1.0.0}declar'))) if 'RefAttachmentHeaderList' in res: files = {} attach_head_list = objectify.fromstring( self.__xml_part(res, b'RefAttachmentHeaderList')) for head in attach_head_list.getchildren(): files[head.uuid] = {'MimeType': head.MimeType} attach_list = objectify.fromstring( self.__xml_part(res, b'FSAttachmentsList')) for attach in attach_list.getchildren(): files[attach.uuid]['UserName'] = str(attach.UserName) files[attach.uuid]['Password'] = str(attach.Password) files[attach.uuid]['FileName'] = str(attach.FileName) for uuid, file in files.items(): file_name = file['FileName'] fn, ext = path.splitext(file_name) res = self.__load_file(uuid, file['UserName'], file['Password'], file['FileName']) if isinstance(res, (str, bytes)): new_ext = guess_extension(file_name).lower() ext = ext.lower() if ext != new_ext: file_name = fn + new_ext else: res, e = res file_name = fn + '.txt' declar.files.append({res: file_name}) uuid = xml.find('.//{*}MessageID').text reply_to = xml.find('.//{*}ReplyTo') if reply_to: reply_to = reply_to.text if hasattr(res, 'Request') \ and hasattr(res.Request, 'SenderProvidedRequestData') \ and hasattr(res.Request.SenderProvidedRequestData, 'MessagePrimaryContent') \ and res.Request.SenderProvidedRequestData.MessagePrimaryContent: # declar = Declar.parsexml( # etree.tostring( # res.Request.SenderProvidedRequestData.MessagePrimaryContent.find( # './/{urn://augo/smev/uslugi/1.0.0}declar'))) declar = Declar.parsexml( etree.tostring(res.Request.SenderProvidedRequestData. MessagePrimaryContent._value_1)) if hasattr(res.Request, 'FSAttachmentsList') \ and res.Request.FSAttachmentsList: attach_head_list = res.Request.SenderProvidedRequestData.RefAttachmentHeaderList for head in attach_head_list: files[head.uuid] = {'MimeType': head.MimeType} attach_list = res.Request.FSAttachmentsList for attach in attach_list: files[attach.uuid]['UserName'] = str(attach.UserName) files[attach.uuid]['Password'] = str(attach.Password) files[attach.uuid]['FileName'] = str(attach.FileName) for uuid, file in files.items(): file_name = file['FileName'] fn, ext = path.splitext(file_name) res = self.__load_file(uuid, file['UserName'], file['Password'], file['FileName']) if isinstance(res, (str, bytes)): new_ext = guess_extension(file_name).lower() ext = ext.lower() if ext != new_ext: file_name = fn + new_ext else: res, e = res file_name = fn + '.txt' # declar.files.append({res: file_name}) files[file_name] = res uuid = res.Request.SenderProvidedRequestData.MessageID reply_to = res.Request.ReplyTo if uuid: operation = 'Ack' tm = etree.Element('AckTargetMessage', Id='SIGNED_BY_CALLER', accepted='true') tm.text = uuid node = self.proxy.create_message( self.proxy.service, operation, tm, CallerInformationSystemSignature=etree.Element('Signature')) res = node.find('.//{*}AckTargetMessage') res.set('Id', 'SIGNED_BY_CALLER') res.set('accepted', 'true') res.text = uuid node_str = etree.tostring(node) self.log.debug(node_str) res = self.__xml_part(node_str, b'ns1:AckTargetMessage') res = self.__call_sign(res) res = node_str.decode().replace('<Signature/>', res) res = self.__send(operation, res) self.log.debug(res) return declar, uuid, reply_to, files def get_response(self, uri="", local_name="", node_id=None, gen_xml_only=False): operation = 'GetResponse' timestamp = datetime.now() node = self.proxy.create_message( self.proxy.service, operation, { 'NamespaceURI': uri, 'RootElementLocalName': local_name, 'Timestamp': timestamp, 'NodeID': node_id }, CallerInformationSystemSignature=etree.Element('Signature')) node[0][0][0].set('Id', 'SIGNED_BY_CALLER') node_str = etree.tostring(node) self.log.debug(node_str) res = self.__call_sign( self.__xml_part(node_str, b'ns1:MessageTypeSelector')) res = node_str.decode().replace('<Signature/>', res) if gen_xml_only: return res res = self.__send(operation, res) self.log.debug(type(res)) self.log.debug(res) request, uuid, reply_to, files = None, None, None, {} if hasattr(res, 'Response') \ and hasattr(res.Response, 'SenderProvidedResponseData'): request = res.Response.SenderProvidedResponseData if hasattr(res.Response.SenderProvidedResponseData, 'MessagePrimaryContent') \ and res.Response.SenderProvidedResponseData.MessagePrimaryContent: # request = RequestResponse.parsexml( # etree.tostring( # res.Response.SenderProvidedResponseData.MessagePrimaryContent._value_1)) res1 = RequestResponse.parsexml( etree.tostring(res.Response.SenderProvidedResponseData. MessagePrimaryContent._value_1)) request.MessagePrimaryContent._value_1 = str(res1) if hasattr(res.Response, 'FSAttachmentsList') \ and res.Response.FSAttachmentsList: attach_head_list = res.Response.SenderProvidedResponseData.RefAttachmentHeaderList for head in attach_head_list: files[head.uuid] = {'MimeType': head.MimeType} attach_list = res.Response.FSAttachmentsList for attach in attach_list: files[attach.uuid]['UserName'] = str(attach.UserName) files[attach.uuid]['Password'] = str(attach.Password) files[attach.uuid]['FileName'] = str(attach.FileName) for uuid, file in files.items(): file_name = file['FileName'] fn, ext = path.splitext(file_name) res = self.__load_file(uuid, file['UserName'], file['Password'], file['FileName']) if isinstance(res, (str, bytes)): new_ext = guess_extension(file_name).lower() ext = ext.lower() if ext != new_ext: file_name = fn + new_ext else: res, e = res file_name = fn + '.txt' files[file_name] = res uuid = res.Response.SenderProvidedResponseData.MessageID reply_to = res.Response.SenderProvidedResponseData.To if uuid: operation = 'Ack' tm = etree.Element('AckTargetMessage', Id='SIGNED_BY_CALLER', accepted='true') tm.text = uuid node = self.proxy.create_message( self.proxy.service, operation, tm, CallerInformationSystemSignature=etree.Element('Signature')) res = node.find('.//{*}AckTargetMessage') res.set('Id', 'SIGNED_BY_CALLER') res.set('accepted', 'true') res.text = uuid node_str = etree.tostring(node) self.log.debug(node_str) res = self.__xml_part(node_str, b'ns1:AckTargetMessage') res = self.__call_sign(res) res = node_str.decode().replace('<Signature/>', res) res = self.__send(operation, res) self.log.debug(res) return request, uuid, reply_to, files def __add_element(self, parent, ns, elem, data, file_names=list()): if not data: return se = etree.SubElement(parent, '{%s}%s' % (ns, elem)) if elem == 'AppliedDocument': if isinstance(data, list): for itm in data: for item in ('title', 'number', 'date', 'valid_until', 'file_name', 'url', 'url_valid_until'): if item in itm and itm[item]: if item == 'file_name': fn = itm[item] file_names.append(fn) self.__add_element(se, ns, item, path.basename(fn), file_names) else: self.__add_element(se, ns, item, itm[item], file_names) if data.index(itm) < len(data) - 1: se = etree.SubElement(parent, '{%s}%s' % (ns, elem)) else: for item in ('title', 'number', 'date', 'valid_until', 'file_name', 'url', 'url_valid_until'): if item in data and data[item]: if item == 'file_name': file_names.append(item) self.__add_element(se, ns, item, data[item], file_names) elif elem == 'legal_entity': if isinstance(data, list): for itm in data: for item in ('name', 'full_name', 'inn', 'kpp', 'address', 'ogrn', 'taxRegDoc', 'govRegDoc', 'govRegDate', 'phone', 'email', 'bossFio', 'buhFio', 'bank', 'bankAccount', 'lastCtrlDate', 'opf', 'govRegOgv', 'person'): if item in itm and itm[item]: self.__add_element(se, ns, item, itm[item], file_names) if data.index(itm) < len(data) - 1: se = etree.SubElement(parent, '{%s}%s' % (ns, elem)) else: for item in ('name', 'full_name', 'inn', 'kpp', 'address', 'ogrn', 'taxRegDoc', 'govRegDoc', 'govRegDate', 'phone', 'email', 'bossFio', 'buhFio', 'bank', 'bankAccount', 'lastCtrlDate', 'opf', 'govRegOgv', 'person'): if item in data and data[item]: self.__add_element(se, ns, item, data[item], file_names) elif 'address' in elem: for item in ('Postal_Code', 'Region', 'District', 'City', 'Urban_District', 'Soviet_Village', 'Locality', 'Street', 'House', 'Reference_point', 'Housing', 'Building', 'Apartment'): if item in data and data[item]: self.__add_element(se, ns, item, data[item], file_names) elif elem in ('person', 'confidant'): if isinstance(data, list): for itm in data: for item in ('surname', 'first_name', 'patronymic', 'address', 'fact_address', 'email', 'birthdate', 'passport_serial', 'passport_number', 'passport_agency', 'passport_date', 'phone', 'inn', 'sex', 'snils'): if item in itm and itm[item]: self.__add_element(se, ns, item, itm[item], file_names) if data.index(itm) < len(data) - 1: se = etree.SubElement(parent, '{%s}%s' % (ns, elem)) else: for item in ('surname', 'first_name', 'patronymic', 'address', 'fact_address', 'email', 'birthdate', 'passport_serial', 'passport_number', 'passport_agency', 'passport_date', 'phone', 'inn', 'sex', 'snils'): if item in data and data[item]: self.__add_element(se, ns, item, data[item], file_names) else: if isinstance(data, (date, datetime)): se.text = data.strftime('%Y-%m-%d') else: se.text = data # if isinstance(data, dict): # se = etree.SubElement(parent, '{%s}%s' % (ns, elem)) # for k, v in data.items(): # if not v: # continue # self.__add_element(se, ns, k, v) # elif isinstance(data, (list, tuple)): # for v in data: # if not isinstance(v, (dict, list, tuple)): # etree.SubElement(parent, '{%s}%s' % (ns, elem)).text = v # else: # se = etree.SubElement(parent, '{%s}%s' % (ns, elem)) # self.__add_element(se, ns, elem, v) # elif isinstance(data, (date, datetime)): # etree.SubElement(parent, '{%s}%s' % (ns, elem)).text = \ # data.strftime('%Y-%m-%d') # else: # etree.SubElement(parent, '{%s}%s' % (ns, elem)).text = data def send_request(self, declar): operation = 'SendRequest' file_names = [] element = self.proxy.get_element('ns1:MessagePrimaryContent') rr = etree.Element('{urn://augo/smev/uslugi/1.0.0}declar', nsmap={'ns1': 'urn://augo/smev/uslugi/1.0.0'}) self.log.debug(declar) for item in ('declar_number', 'service', 'register_date', 'end_date', 'object_address', 'AppliedDocument', 'legal_entity', 'person', 'confidant', 'Param'): if item in declar and declar[item]: self.__add_element(rr, 'urn://augo/smev/uslugi/1.0.0', item, declar[item], file_names) # for k, v in declar.items(): # if isinstance(v, list): # for val in v: # se = etree.SubElement( # rr, '{urn://augo/smev/uslugi/1.0.0}%s' % k) # for n, m in val.items(): # if not m: # continue # if n == 'file_name': # file_names.append(m) # else: # self.__add_element( # se, 'urn://augo/smev/uslugi/1.0.0', n, m) # elif isinstance(v, dict): # se = etree.SubElement( # rr, '{urn://augo/smev/uslugi/1.0.0}%s' % k) # for n, m in v.items(): # if not m: # continue # self.__add_element( # se, 'urn://augo/smev/uslugi/1.0.0', n, m) # else: # if not v: # continue # self.__add_element( # rr, 'urn://augo/smev/uslugi/1.0.0', k, v) mpc = element(rr) node = self.proxy.create_message( self.proxy.service, operation, { 'MessageID': uuid1(), 'MessagePrimaryContent': mpc }, CallerInformationSystemSignature=etree.Element('Signature')) res = node.find('.//{*}SenderProvidedRequestData') res.set('Id', 'SIGNED_BY_CALLER') if file_names: ns = etree.QName( node.find('.//{*}MessagePrimaryContent')).namespace rahl = etree.SubElement(res, '{%s}RefAttachmentHeaderList' % ns) for file_name in file_names: rah = etree.SubElement(rahl, '{%s}RefAttachmentHeader' % ns) etree.SubElement(rah, '{%s}uuid' % ns).text = self.__upload_file( file_name, translate(basename(file_name))) f_hash = self.crypto.get_file_hash(file_name) etree.SubElement(rah, '{%s}Hash' % ns).text = f_hash etree.SubElement(rah, '{%s}MimeType' % ns).text = guess_type(file_name)[0] # etree.SubElement( # rah, # '{%s}SignaturePKCS7' % ns).text = self.crypto.get_file_sign( # file_name) # Mark request as test request ns = etree.QName( node.find('.//{*}SenderProvidedRequestData')).namespace etree.SubElement(res, '{%s}TestMessage' % ns) node_str = etree.tostring(node) res = etree.QName(res) node_str = node_str.replace( b'<ns0:SenderProvidedRequestData', b'<ns0:SenderProvidedRequestData xmlns:ns0="' + res.namespace.encode() + b'"') self.log.debug(node_str) res = self.__xml_part(node_str, b'ns0:SenderProvidedRequestData') res = self.__call_sign(res) res = node_str.decode().replace('<Signature/>', res) self.log.debug(res) res = self.__send(operation, res.encode('utf-8')) self.log.debug(res) return res def send_response(self, reply_to, declar_number, register_date, result='FINAL', text='', applied_documents=list(), ftp_user='', ftp_pass=''): files = [] for doc in applied_documents: if isinstance(doc, (bytes, str)): file_name = os.path.split(doc)[1] uuid = self.__upload_file(doc, file_name, ftp_user, ftp_pass) files.append({ uuid: { 'name': file_name, 'type': guess_type(doc)[0], 'full_name': doc } }) if doc.file: uuid = self.__upload_file(doc.file, doc.file_name, ftp_user, ftp_pass) files.append({ uuid: { 'name': doc.file_name, 'type': guess_type(doc.file)[0], 'full_name': doc.file } }) operation = 'SendResponse' element = self.proxy.get_element('ns1:MessagePrimaryContent') rr = etree.Element('{urn://augo/smev/uslugi/1.0.0}requestResponse', nsmap={'ns1': 'urn://augo/smev/uslugi/1.0.0'}) etree.SubElement( rr, '{urn://augo/smev/uslugi/1.0.0}declar_number').text = declar_number etree.SubElement( rr, '{urn://augo/smev/uslugi/1.0.0}register_date').text = \ register_date.strftime('%Y-%m-%d') if isinstance( register_date, (date, datetime)) else register_date etree.SubElement(rr, '{urn://augo/smev/uslugi/1.0.0}result').text = result if text: etree.SubElement(rr, '{urn://augo/smev/uslugi/1.0.0}text').text = text if files: for doc in applied_documents: ad = etree.SubElement( rr, '{urn://augo/smev/uslugi/1.0.0}AppliedDocument') etree.SubElement( ad, '{urn://augo/smev/uslugi/1.0.0}title').text = \ doc.title etree.SubElement( ad, '{urn://augo/smev/uslugi/1.0.0}number').text = \ doc.number etree.SubElement( ad, '{urn://augo/smev/uslugi/1.0.0}date').text = \ doc.date etree.SubElement( ad, '{urn://augo/smev/uslugi/1.0.0}valid_until').text = \ doc.valid_until etree.SubElement( ad, '{urn://augo/smev/uslugi/1.0.0}file_name').text = \ doc.file_name etree.SubElement( ad, '{urn://augo/smev/uslugi/1.0.0}url').text = doc.url etree.SubElement( ad, '{urn://augo/smev/uslugi/1.0.0}url_valid_until').text = \ doc.url_valid_until mpc = element(rr) node = self.proxy.create_message( self.proxy.service, operation, { 'MessageID': uuid1(), 'To': reply_to, 'MessagePrimaryContent': mpc }, CallerInformationSystemSignature=etree.Element('Signature')) res = node.find('.//{*}SenderProvidedResponseData') res.set('Id', 'SIGNED_BY_CALLER') if files: ns = etree.QName( node.find('.//{*}MessagePrimaryContent')).namespace rahl = etree.SubElement(res, '{%s}RefAttachmentHeaderList' % ns) for uuid, file in files: rah = etree.SubElement(rahl, '{%s}RefAttachmentHeader' % ns) etree.SubElement(rah, '{%s}uuid' % ns).text = uuid etree.SubElement(rah, '{%s}Hash' % ns).text = self.crypto.get_file_hash( file['full_name']) etree.SubElement(rah, '{%s}MimeType' % ns).text = file['type'] # etree.SubElement( # rah, # '{%s}SignaturePKCS7' % ns).text = self.crypto.get_file_sign( # file['full_name']) node_str = etree.tostring(node) res = etree.QName(res) node_str = node_str.replace( b'<ns0:SenderProvidedResponseData', b'<ns0:SenderProvidedResponseData xmlns:ns0="' + res.namespace.encode() + b'"') self.log.debug(node_str) res = self.__xml_part(node_str, b'ns0:SenderProvidedResponseData') res = self.__call_sign(res) res = node_str.decode().replace('<Signature/>', res) res = self.__send(operation, res.encode('utf-8')) self.log.debug(res) return res def __call_sign(self, xml): method_name = 'sign_' + self.method self.log.debug('Calling Crypto.%s' % method_name) method = getattr(self.crypto, method_name) return method(xml) def __upload_file(self, file, file_name, ftp_user='******', ftp_pass='******'): self.log.debug(file_name) addr = urlparse(self.ftp_addr).netloc with ftplib.FTP(addr, ftp_user, ftp_pass) as con: uuid = str(uuid1()) res = con.mkd(uuid) self.log.debug(res) con.cwd(uuid) with open(file, 'rb') as f: res = con.storbinary('STOR ' + file_name, f) self.log.debug(res) return uuid def __load_file(self, uuid, user, passwd, file_name): addr = urlparse(self.ftp_addr).netloc f, file_path = tempfile.mkstemp() try: with ftplib.FTP(addr, user, passwd) as con: con.cwd(uuid) if file_name[0] == '/': file_name = file_name[1:] close(f) with open(file_path, 'wb') as f: con.retrbinary('RETR ' + file_name, f.write) except ftplib.all_errors as e: self.log.error(str(e)) write(f, str(e).encode('cp1251')) close(f) return file_path, e return file_path def __send(self, operation, msg): kw = {'_soapheaders': self.proxy.service._client._default_soapheaders} response = self.proxy.transport.post( self.proxy.service._binding_options['address'], msg, kw) res = self.proxy.service._binding.process_reply( self.proxy.service._client, self.proxy.service._binding.get(operation), response) # if not res and b'--uuid:' in response.content: # res = response.content[ # response.content.index(b'Content-Type:'):] # res = res[:res.index(b'--uuid:')] # self.log.debug(res) return res def __xml_part(self, xml_as_str, tag_name): """ Cuts the XML part from `xml_as_str` bounded by tag `tag_name` :param xml_as_str: String with source XML :param tag_name: XML tag name bounds target XML part """ b_idx = xml_as_str.index(tag_name) - 1 try: if isinstance(tag_name, str): tgn = tag_name + '>' else: tgn = tag_name + b'>' e_idx = xml_as_str.index(tgn, b_idx + len(tag_name)) + len(tag_name) + 1 except ValueError: if isinstance(tag_name, str): tgn = tag_name + ' ' else: tgn = tag_name + b' ' e_idx = xml_as_str.index(tgn, b_idx + len(tag_name)) + len(tag_name) + 1 return xml_as_str[b_idx:e_idx]
class XMLAConnection(object): @classmethod def addMethod(cls, funcname, func): return setattr(cls, funcname, func) @classmethod def setupMembers(cls): def getFunc(schemaName): return lambda this, *args, **kw: cls.Discover(this, schemaName, *args, **kw) for schemaName in xmla1_1_rowsets: mname = schemaNameToMethodName(schemaName) cls.addMethod( mname, getFunc(schemaName) ) def __init__(self, url, location, sslverify, **kwargs): if "session" in kwargs: session = kwargs["session"] del kwargs["session"] transport = Transport(session=session) else: transport = Transport() if "auth" in kwargs: transport.session.auth = kwargs["auth"] del kwargs["auth"] transport.session.verify = sslverify self.sessionplugin=SessionPlugin(self) plugins=[self.sessionplugin] if "log" in kwargs: log = kwargs.get("log") if isinstance(log, Plugin): plugins.append(log) elif log == True: plugins.append(LogRequest()) del kwargs["log"] self.client = Client(url, transport=transport, # cache=None, unwrap=False, plugins=plugins) self.service = self.client.create_service(ns_name(schema_xmla,"MsXmlAnalysisSoap"), location) self.client.set_ns_prefix(None, schema_xmla) # optional, call might fail self.getMDSchemaLevels = lambda *args, **kw: self.Discover("MDSCHEMA_LEVELS", *args, **kw) self.setListenOnSessionId(False) self.setSessionId(None) self._soapheaders=None def getListenOnSessionId(self): return self.listenOnSessionId def setListenOnSessionId(self, trueOrFalse): self.listenOnSessionId = trueOrFalse def setSessionId(self, sessionId): self.sessionId = sessionId def Discover(self, what, restrictions=None, properties=None): rl = as_etree(restrictions, "RestrictionList") pl = as_etree(properties, "PropertyList") try: #import pdb; pdb.set_trace() doc=self.service.Discover(RequestType=what, Restrictions=rl, Properties=pl, _soapheaders=self._soapheaders) root = fromETree(doc.body["return"]["_value_1"], ns=schema_xmla_rowset) res = getattr(root, "row", []) if res: res = aslist(res) except Fault as fault: raise XMLAException(fault.message, dictify(fromETree(fault.detail, ns=None))) #logger.debug( res ) return res def Execute(self, command, dimformat="Multidimensional", axisFormat="TupleFormat", **kwargs): if isinstance(command, stringtypes): command=as_etree({"Statement": command}) props = {"Format":dimformat, "AxisFormat":axisFormat} props.update(kwargs) plist = as_etree({"PropertyList":props}) try: res = self.service.Execute(Command=command, Properties=plist, _soapheaders=self._soapheaders) root = res.body["return"]["_value_1"] return TupleFormatReader(fromETree(root, ns=schema_xmla_mddataset)) except Fault as fault: raise XMLAException(fault.message, dictify(fromETree(fault.detail, ns=None))) def BeginSession(self): bs= self.client.get_element(ns_name(schema_xmla,"BeginSession"))(mustUnderstand=1) self.setListenOnSessionId(True) cmd = as_etree("Statement") self.service.Execute(Command=cmd,_soapheaders={"BeginSession":bs}) self.setListenOnSessionId(False) sess= self.client.get_element(ns_name(schema_xmla,"Session"))(SessionId=self.sessionId, mustUnderstand = 1) self._soapheaders={"Session":sess} def EndSession(self): if self.sessionId is not None: es= self.client.get_element(ns_name(schema_xmla,"EndSession"))(SessionId=self.sessionId, mustUnderstand = 1) cmd = as_etree("Statement") self.service.Execute(Command=cmd, _soapheaders={"EndSession":es}) self.setSessionId(None) self._soapheaders=None
def webhook(): chatresponse = {} try: #get the fulfilnment request fulfilmentrequest = request.data fulfilmentrequest = json.loads(fulfilmentrequest.decode('UTF-8')) intentname = fulfilmentrequest['result']['metadata']['intentName'] if intentname == 'warranty_check': client = Client( 'https://apiaiwebhookpassthough.herokuapp.com/static/content/EntitlementService.xml' ) requesttype = client.get_element('ns2:EntitlementRequest') entitlementrequest = requesttype( BusinessOrgID=1, ProductTypeCode='P', RequestedByUserID='26may2017', SerialNumber=fulfilmentrequest['result']['parameters']['slno'], SymptomIDList='0707') response = client.service.Entitle(request=entitlementrequest) warrantyexpirydate = response['Entitlement']['WarrantyExpiryDate'] if datetime.now() < warrantyexpirydate: chatresponse['displayText'] = "The phone is under warranty" chatresponse['speech'] = "Your phone is under warranty" else: chatresponse['displayText'] = "The phone is not under warranty" chatresponse['speech'] = "Your phone is not under warranty" elif intentname == 'warranty_expiry_date': client = Client( 'https://apiaiwebhookpassthough.herokuapp.com/static/content/EntitlementService.xml' ) requesttype = client.get_element('ns2:EntitlementRequest') entitlementrequest = requesttype( BusinessOrgID=1, ProductTypeCode='P', RequestedByUserID='26may2017', SerialNumber=fulfilmentrequest['result']['parameters']['slno'], SymptomIDList='0707') response = client.service.Entitle(request=entitlementrequest) warrantyexpirydate = response['Entitlement']['WarrantyExpiryDate'] if datetime.now() < warrantyexpirydate: chatresponse[ 'displayText'] = "The warranty is valid till {}".format( warrantyexpirydate) chatresponse[ 'speech'] = "Your phone is under warranty till {}".format( warrantyexpirydate) else: chatresponse['displayText'] = "The phone is not under warranty" chatresponse['speech'] = "Your phone is not under warranty" elif intentname == 'warranty_valid_duration': client = Client( 'https://apiaiwebhookpassthough.herokuapp.com/static/content/EntitlementService.xml' ) requesttype = client.get_element('ns2:EntitlementRequest') entitlementrequest = requesttype( BusinessOrgID=1, ProductTypeCode='P', RequestedByUserID='26may2017', SerialNumber=fulfilmentrequest['result']['parameters']['slno'], SymptomIDList='0707') response = client.service.Entitle(request=entitlementrequest) warrantyexpirydate = response['Entitlement']['WarrantyExpiryDate'] if datetime.now() < warrantyexpirydate: warrantyvaliddays = (warrantyexpirydate - datetime.today()).days chatresponse[ 'displayText'] = "The warranty is valid for next {} days".format( warrantyvaliddays) chatresponse[ 'speech'] = "Your phone is under warranty for {} days".format( warrantyvaliddays) else: chatresponse['displayText'] = "The phone is not under warranty" chatresponse['speech'] = "Your phone is not under warranty" except Exception as e: chatresponse[ 'displayText'] = "Unable to find warranty details! Please check the serial number and try again" chatresponse['speech'] = "Check the serial number and try again" return json.dumps(chatresponse)
class SfdcMetadataApi: # pylint: disable=too-many-instance-attributes """ Class to work with Salesforce Metadata API """ _METADATA_API_BASE_URI = "/services/Soap/m/{version}" _XML_NAMESPACES = { 'soapenv': 'http://schemas.xmlsoap.org/soap/envelope/', 'mt': 'http://soap.sforce.com/2006/04/metadata' } # pylint: disable=R0913 def __init__(self, session, session_id, instance, metadata_url, headers, api_version): """ Initialize and check session """ self.session = session self._session_id = session_id self._instance = instance self.metadata_url = metadata_url self.headers = headers self._api_version = api_version self._deploy_zip = None wsdl_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'metadata.wsdl') self._client = Client( os.path.join('simple_salesforce', wsdl_path), settings=Settings(strict=False, xsd_ignore_sequence_order=True)) self._service = self._client.create_service( "{http://soap.sforce.com/2006/04/metadata}MetadataBinding", self.metadata_url) self._session_header = self._client.get_element('ns0:SessionHeader')( sessionId=self._session_id) def __getattr__(self, item): return MetadataType(item, self._service, self._client.get_type('ns0:' + item), self._session_header) def describe_metadata(self): """ Performs a describeMetadata call :returns: An object of zeep.objects.DescribeMetadataResult """ return self._service.describeMetadata( self._api_version, _soapheaders=[self._session_header]) def list_metadata(self, queries): """ Performs a listMetadata call :param queries: A list of zeep.objects.ListMetadataQuery that specify which components you are interested in. Limit: 3 :type queries: list :returns: List of zeep.objects.FileProperties objects :rtype: list """ return self._service.listMetadata(queries, self._api_version, _soapheaders=[self._session_header]) # pylint: disable=R0914 # pylint: disable-msg=C0103 def deploy(self, zipfile, sandbox, **kwargs): """ Kicks off async deployment, returns deployment id :param zipfile: :type zipfile: :param kwargs: :type kwargs: :return: :rtype: """ client = kwargs.get('client', 'simple_salesforce_metahelper') checkOnly = kwargs.get('checkOnly', False) testLevel = kwargs.get('testLevel') tests = kwargs.get('tests') ignoreWarnings = kwargs.get('ignoreWarnings', False) allowMissingFiles = kwargs.get('allowMissingFiles', False) autoUpdatePackage = kwargs.get('autoUpdatePackage', False) performRetrieve = kwargs.get('performRetrieve', False) purgeOnDelete = kwargs.get('purgeOnDelete', False) rollbackOnError = kwargs.get('rollbackOnError', False) singlePackage = True attributes = { 'client': client, 'checkOnly': checkOnly, 'sessionId': self._session_id, 'ZipFile': self._read_deploy_zip(zipfile), 'testLevel': testLevel, 'tests': tests, 'ignoreWarnings': ignoreWarnings, 'allowMissingFiles': allowMissingFiles, 'autoUpdatePackage': autoUpdatePackage, 'performRetrieve': performRetrieve, 'purgeOnDelete': purgeOnDelete, 'rollbackOnError': rollbackOnError, 'singlePackage': singlePackage, } if not sandbox: attributes['allowMissingFiles'] = False attributes['rollbackOnError'] = True if testLevel: test_level = "<met:testLevel>%s</met:testLevel>" % testLevel attributes['testLevel'] = test_level tests_tag = '' if tests and \ str(testLevel).lower() == 'runspecifiedtests': for test in tests: tests_tag += '<met:runTests>%s</met:runTests>\n' % test attributes['tests'] = tests_tag request = DEPLOY_MSG.format(**attributes) headers = {'Content-Type': 'text/xml', 'SOAPAction': 'deploy'} result = call_salesforce(url=self.metadata_url + 'deployRequest', method='POST', session=self.session, headers=self.headers, additional_headers=headers, data=request) async_process_id = ET.fromstring(result.text).find( 'soapenv:Body/mt:deployResponse/mt:result/mt:id', self._XML_NAMESPACES).text state = ET.fromstring(result.text).find( 'soapenv:Body/mt:deployResponse/mt:result/mt:state', self._XML_NAMESPACES).text return async_process_id, state @staticmethod # pylint: disable=R1732 def _read_deploy_zip(zipfile): """ :param zipfile: :type zipfile: :return: :rtype: """ if hasattr(zipfile, 'read'): file = zipfile file.seek(0) should_close = False else: file = open(zipfile, 'rb') should_close = True raw = file.read() if should_close: file.close() return b64encode(raw).decode("utf-8") def _retrieve_deploy_result(self, async_process_id, **kwargs): """ Retrieves status for specified deployment id :param async_process_id: :type async_process_id: :param kwargs: :type kwargs: :return: :rtype: """ client = kwargs.get('client', 'simple_salesforce_metahelper') attributes = { 'client': client, 'sessionId': self._session_id, 'asyncProcessId': async_process_id, 'includeDetails': 'true' } mt_request = CHECK_DEPLOY_STATUS_MSG.format(**attributes) headers = { 'Content-type': 'text/xml', 'SOAPAction': 'checkDeployStatus' } res = call_salesforce(url=self.metadata_url + 'deployRequest/' + async_process_id, method='POST', session=self.session, headers=self.headers, additional_headers=headers, data=mt_request) root = ET.fromstring(res.text) result = root.find( 'soapenv:Body/mt:checkDeployStatusResponse/mt:result', self._XML_NAMESPACES) if result is None: raise Exception("Result node could not be found: %s" % res.text) return result @staticmethod def get_component_error_count(value): """Get component error counts""" try: return int(value) except ValueError: return 0 def check_deploy_status(self, async_process_id, **kwargs): """ Checks whether deployment succeeded :param async_process_id: :type async_process_id: :param kwargs: :type kwargs: :return: :rtype: """ result = self._retrieve_deploy_result(async_process_id, **kwargs) state = result.find('mt:status', self._XML_NAMESPACES).text state_detail = result.find('mt:stateDetail', self._XML_NAMESPACES) if state_detail is not None: state_detail = state_detail.text unit_test_errors = [] deployment_errors = [] failed_count = self.get_component_error_count( result.find('mt:numberComponentErrors', self._XML_NAMESPACES).text) if state == 'Failed' or failed_count > 0: # Deployment failures failures = result.findall('mt:details/mt:componentFailures', self._XML_NAMESPACES) for failure in failures: deployment_errors.append({ 'type': failure.find('mt:componentType', self._XML_NAMESPACES).text, 'file': failure.find('mt:fileName', self._XML_NAMESPACES).text, 'status': failure.find('mt:problemType', self._XML_NAMESPACES).text, 'message': failure.find('mt:problem', self._XML_NAMESPACES).text }) # Unit test failures failures = result.findall( 'mt:details/mt:runTestResult/mt:failures', self._XML_NAMESPACES) for failure in failures: unit_test_errors.append({ 'class': failure.find('mt:name', self._XML_NAMESPACES).text, 'method': failure.find('mt:methodName', self._XML_NAMESPACES).text, 'message': failure.find('mt:message', self._XML_NAMESPACES).text, 'stack_trace': failure.find('mt:stackTrace', self._XML_NAMESPACES).text }) deployment_detail = { 'total_count': result.find('mt:numberComponentsTotal', self._XML_NAMESPACES).text, 'failed_count': result.find('mt:numberComponentErrors', self._XML_NAMESPACES).text, 'deployed_count': result.find('mt:numberComponentsDeployed', self._XML_NAMESPACES).text, 'errors': deployment_errors } unit_test_detail = { 'total_count': result.find('mt:numberTestsTotal', self._XML_NAMESPACES).text, 'failed_count': result.find('mt:numberTestErrors', self._XML_NAMESPACES).text, 'completed_count': result.find('mt:numberTestsCompleted', self._XML_NAMESPACES).text, 'errors': unit_test_errors } return state, state_detail, deployment_detail, unit_test_detail def download_unit_test_logs(self, async_process_id): """ Downloads Apex logs for unit tests executed during specified deployment """ result = self._retrieve_deploy_result(async_process_id) print("response: %s" % ET.tostring(result, encoding="us-ascii", method="xml")) def retrieve(self, async_process_id, **kwargs): """ Submits retrieve request """ # Compose unpackaged XML client = kwargs.get('client', 'simple_salesforce_metahelper') single_package = kwargs.get('single_package', True) if not isinstance(single_package, bool): raise TypeError('single_package must be bool') unpackaged = '' if kwargs.get('unpackaged'): for metadata_type in kwargs.get('unpackaged'): if isinstance(kwargs.get('unpackaged'), dict): members = kwargs.get('unpackaged')[metadata_type] unpackaged += '<types>' for member in members: unpackaged += '<members>{member}</members>'.format( member=member) unpackaged += '<name>{metadata_type}</name></types>'.format( metadata_type=metadata_type) else: raise TypeError('unpackaged metadata types must be a dict') # Compose retrieve request XML attributes = { 'client': client, 'sessionId': self._session_id, 'apiVersion': self._api_version, 'singlePackage': single_package, 'unpackaged': unpackaged } request = RETRIEVE_MSG.format(**attributes) # Submit request headers = {'Content-type': 'text/xml', 'SOAPAction': 'retrieve'} res = call_salesforce(url=self.metadata_url + 'deployRequest/' + async_process_id, method='POST', session=self.session, headers=self.headers, additional_headers=headers, data=request) # Parse response to get async Id and status async_process_id = ET.fromstring(res.text).find( 'soapenv:Body/mt:retrieveResponse/mt:result/mt:id', self._XML_NAMESPACES).text state = ET.fromstring(res.text).find( 'soapenv:Body/mt:retrieveResponse/mt:result/mt:state', self._XML_NAMESPACES).text return async_process_id, state def retrieve_retrieve_result(self, async_process_id, include_zip, **kwargs): """ Retrieves status for specified retrieval id """ client = kwargs.get('client', 'simple_salesforce_metahelper') attributes = { 'client': client, 'sessionId': self._session_id, 'asyncProcessId': async_process_id, 'includeZip': include_zip } mt_request = CHECK_RETRIEVE_STATUS_MSG.format(**attributes) headers = { 'Content-type': 'text/xml', 'SOAPAction': 'checkRetrieveStatus' } res = call_salesforce(url=self.metadata_url + 'deployRequest/' + async_process_id, method='POST', session=self.session, headers=self.headers, additional_headers=headers, data=mt_request) root = ET.fromstring(res.text) result = root.find( 'soapenv:Body/mt:checkRetrieveStatusResponse/mt:result', self._XML_NAMESPACES) if result is None: raise Exception("Result node could not be found: %s" % res.text) return result def retrieve_zip(self, async_process_id, **kwargs): """ Retrieves ZIP file """ result = self.retrieve_retrieve_result(async_process_id, 'true', **kwargs) state = result.find('mt:status', self._XML_NAMESPACES).text error_message = result.find('mt:errorMessage', self._XML_NAMESPACES) if error_message is not None: error_message = error_message.text # Check if there are any messages messages = [] message_list = result.findall('mt:details/mt:messages', self._XML_NAMESPACES) for message in message_list: messages.append({ 'file': message.find('mt:fileName', self._XML_NAMESPACES).text, 'message': message.find('mt:problem', self._XML_NAMESPACES).text }) # Retrieve base64 encoded ZIP file zipfile_base64 = result.find('mt:zipFile', self._XML_NAMESPACES).text zipfile = b64decode(zipfile_base64) return state, error_message, messages, zipfile def check_retrieve_status(self, async_process_id, **kwargs): """ Checks whether retrieval succeeded """ result = self.retrieve_retrieve_result(async_process_id, 'false', **kwargs) state = result.find('mt:status', self._XML_NAMESPACES).text error_message = result.find('mt:errorMessage', self._XML_NAMESPACES) if error_message is not None: error_message = error_message.text # Check if there are any messages messages = [] message_list = result.findall('mt:details/mt:messages', self._XML_NAMESPACES) for message in message_list: messages.append({ 'file': message.find('mt:fileName', self._XML_NAMESPACES).text, 'message': message.find('mt:problem', self._XML_NAMESPACES).text }) return state, error_message, messages