Coverage for jbank/wsedi.py : 0%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# pylint: disable=logging-format-interpolation,logging-not-lazy,too-many-arguments,too-many-locals,too-many-statements
2import base64
3import logging
4import traceback
5from datetime import date
6from os.path import basename
7from typing import Callable, Optional
8import requests
9from django.conf import settings
10from django.template.loader import get_template
11from django.utils.timezone import now
12from django.utils.translation import ugettext as _
13from zeep.wsse import BinarySignature # type: ignore
14from jbank.models import WsEdiConnection, WsEdiSoapCall
15from lxml import etree # type: ignore # pytype: disable=import-error
18logger = logging.getLogger(__name__)
21def wsedi_get(
22 command: str, file_type: str, status: str, file_reference: str = "", verbose: bool = False
23) -> requests.Response:
24 """
25 Download Finnish bank files. Assumes WS-EDI API parameter compatible HTTP REST API end-point.
26 Uses project settings WSEDI_URL and WSEDI_TOKEN.
27 :param command: Command, e.g. DownloadFileList or DownloadFile
28 :param file_type: File type, e.g. TO or SVM
29 :param status: Status, e.g. DLD or NEW
30 :param file_reference: File reference (if command is DownloadFile)
31 :param verbose: Debug output
32 :return: requests.Response
33 """
34 url = settings.WSEDI_URL + "?command={command}".format(command=command)
35 if file_reference:
36 url += "&file-reference=" + file_reference
37 if file_type:
38 url += "&file-type=" + file_type
39 if status:
40 url += "&status=" + status
41 headers = {
42 "Content-Type": "application/json",
43 "Authorization": "Token " + settings.WSEDI_TOKEN,
44 }
45 res = requests.get(url, headers=headers)
46 if res.status_code >= 300:
47 logger.error(
48 "wsedi_get(command={}, file_type={}, status={}, file_reference={}) response HTTP {}:\n".format(
49 command, file_type, status, file_reference, res.status_code
50 )
51 + res.text
52 )
53 elif verbose:
54 logger.info(
55 "wsedi_get(command={}, file_type={}, status={}, file_reference={}) response HTTP {}:\n".format(
56 command, file_type, status, file_reference, res.status_code
57 )
58 + res.text
59 )
61 if res.status_code >= 300:
62 raise Exception("WS-EDI {} HTTP {}".format(command, res.status_code))
63 return res
66def wsedi_upload_file(file_content: str, file_type: str, file_name: str, verbose: bool = False) -> requests.Response:
67 """
68 Upload Finnish bank file. Assumes WS-EDI API parameter compatible HTTP REST API end-point.
69 Uses project settings WSEDI_URL and WSEDI_TOKEN.
70 :param file_content: File content
71 :param file_type: File type, e.g. pain.001.001.03
72 :param file_name: File (base) name
73 :param verbose: Debug output
74 :return: requests.Response
75 """
76 command = "UploadFile"
77 url = settings.WSEDI_URL
78 data = {
79 "command": command,
80 "file-type": file_type,
81 "file-name": basename(file_name),
82 "file-content": base64.b64encode(file_content.encode("utf8")).decode("ascii"),
83 }
84 headers = {
85 "Content-Type": "application/x-www-form-urlencoded",
86 "Authorization": "Token " + settings.WSEDI_TOKEN,
87 }
88 res = requests.post(url, data=data, headers=headers)
89 if res.status_code >= 300:
90 logger.error(
91 "wsedi_upload_file(command={}, file_type={}, file_name={}) response HTTP {}:\n".format(
92 command, file_type, file_name, res.status_code
93 )
94 + res.text
95 )
96 raise Exception("WS-EDI {} HTTP {}".format(command, res.status_code))
97 if verbose:
98 logger.info(
99 "wsedi_upload_file(command={}, file_type={}, file_name={}) response HTTP {}:\n".format(
100 command, file_type, file_name, res.status_code
101 )
102 + res.text
103 )
104 return res
107def wsedi_execute( # noqa
108 ws: WsEdiConnection,
109 command: str,
110 file_type: str = "",
111 status: str = "",
112 file_reference: str = "", # noqa
113 file_content: str = "",
114 start_date: Optional[date] = None,
115 end_date: Optional[date] = None,
116 verbose: bool = False,
117 cls: Callable = WsEdiSoapCall,
118 **kwargs
119) -> bytes:
120 """
121 :param ws:
122 :param command:
123 :param file_type:
124 :param status:
125 :param file_reference:
126 :param file_content:
127 :param start_date:
128 :param end_date:
129 :param verbose:
130 :param cls:
131 :return: bytes
132 """
133 if ws and not ws.enabled:
134 raise Exception(_("ws.edi.connection.not.enabled").format(ws=ws))
136 soap_call = cls(connection=ws, command=command, **kwargs)
137 soap_call.full_clean()
138 soap_call.save()
139 call_str = "WsEdiSoapCall({})".format(soap_call.id)
140 try:
141 content = ""
142 if file_content:
143 content = base64.b64encode(file_content.encode()).decode("ascii")
145 app = ws.get_application_request(
146 command,
147 file_type=file_type,
148 status=status,
149 file_reference=file_reference,
150 content=content,
151 start_date=start_date,
152 end_date=end_date,
153 )
154 if verbose:
155 logger.info(
156 "------------------------------------------------------ {} app\n{}".format(call_str, app.decode())
157 )
158 debug_output = command in ws.debug_command_list or "ALL" in ws.debug_command_list
159 if debug_output:
160 with open(soap_call.debug_request_full_path, "wb") as fp:
161 fp.write(app)
163 signed_app = ws.sign_application_request(app)
164 if verbose:
165 logger.info(
166 "------------------------------------------------------ {} signed_app\n{}".format(
167 call_str, signed_app.decode()
168 )
169 )
171 if ws.bank_encryption_cert_file:
172 enc_app = ws.encrypt_application_request(signed_app)
173 if verbose:
174 logger.info(
175 "------------------------------------------------------ {} enc_app\n{}".format(
176 call_str, enc_app.decode()
177 )
178 )
179 else:
180 enc_app = signed_app
181 if verbose:
182 logger.info(
183 "------------------------------------------------------ "
184 "{} enc_app\n(no bank_encryption_cert_file, not encrypting)".format(call_str)
185 )
187 b64_app = ws.encode_application_request(enc_app)
188 if verbose:
189 logger.info(
190 "------------------------------------------------------ {} b64_app\n{}".format(
191 call_str, b64_app.decode()
192 )
193 )
195 soap_body = get_template("jbank/soap_template.xml").render(
196 {
197 "soap_call": soap_call,
198 "payload": b64_app.decode(),
199 }
200 )
201 if verbose:
202 logger.info(
203 "------------------------------------------------------ {} soap_body\n{}".format(call_str, soap_body)
204 )
206 body_bytes = soap_body.encode()
207 envelope = etree.fromstring(body_bytes)
208 binary_signature = BinarySignature(ws.signing_key_full_path, ws.signing_cert_full_path)
209 soap_headers: dict = {}
210 envelope, soap_headers = binary_signature.apply(envelope, soap_headers)
211 signed_body_bytes = etree.tostring(envelope)
212 if verbose:
213 logger.info(
214 "------------------------------------------------------ {} signed_body_bytes\n{}".format(
215 call_str, signed_body_bytes
216 )
217 )
219 http_headers = {
220 "Connection": "Close",
221 "Content-Type": "text/xml",
222 "Method": "POST",
223 "SOAPAction": "",
224 "User-Agent": "Kajala WS",
225 }
226 if verbose:
227 logger.info("HTTP POST {}".format(ws.soap_endpoint))
228 res = requests.post(ws.soap_endpoint, data=signed_body_bytes, headers=http_headers)
229 if verbose:
230 logger.info(
231 "------------------------------------------------------ {} HTTP response {}\n{}".format(
232 call_str, res.status_code, res.text
233 )
234 )
235 if res.status_code >= 300:
236 logger.error(
237 "------------------------------------------------------ {} HTTP response {}\n{}".format(
238 call_str, res.status_code, res.text
239 )
240 )
241 raise Exception("WS-EDI {} HTTP {}".format(command, res.status_code))
243 envelope = etree.fromstring(res.content)
244 app_res_el = envelope.find(".//{http://model.bxd.fi}ApplicationResponse")
245 if app_res_el is None:
246 logger.error(
247 "------------------------------------------------------ {} HTTP response {}\n{}".format(
248 call_str, res.status_code, res.text
249 )
250 )
251 raise Exception("WS-EDI {} failed, missing ApplicationResponse".format(command))
252 app_res_enc = ws.decode_application_response(app_res_el.text.encode())
253 if verbose:
254 logger.info(
255 "------------------------------------------------------ {} app_res_enc\n{}".format(
256 call_str, app_res_enc.decode()
257 )
258 )
260 if ws.encryption_key_file:
261 app_res = ws.decrypt_application_response(app_res_enc)
262 if verbose:
263 logger.info(
264 "------------------------------------------------------ {} app_res\n{}".format(
265 call_str, app_res.decode()
266 )
267 )
268 else:
269 app_res = app_res_enc
270 if verbose:
271 logger.info(
272 "------------------------------------------------------ "
273 "{} app_res\n(no encryption_key_file, assuming decrypted content)".format(call_str)
274 )
276 soap_call.executed = now()
277 soap_call.save(update_fields=["executed"])
279 if debug_output:
280 with open(soap_call.debug_response_full_path, "wb") as fp:
281 fp.write(app_res)
283 return app_res
284 except Exception:
285 soap_call.error = traceback.format_exc()
286 soap_call.save(update_fields=["error"])
287 raise