I migrating code from VB.Net to python and faced a problem. In VB.Net we gets an Stream object and write some data there, but i cant find likely methods in python.
VB.Net code
Dim myRequest As System.Net.HttpWebRequest = DirectCast(Net.WebRequest.Create(URLString), Net.HttpWebRequest)
myRequest.Timeout = 6000
myRequest.Method = "POST"
myRequest.KeepAlive = True
Dim pS As String = """"
Dim boundary As String = "boundStr"
myRequest.ContentType = "multipart/form-data; boundary=" + boundary
myRequest.Headers.Add(Net.HttpRequestHeader.Authorization, "Bearer " + access_token)
Console.WriteLine(myRequest.Headers)
Dim stream As System.IO.Stream = myRequest.GetRequestStream()
After we write some information about excel file into a stream and write a file there.
I tried to find somethink in requests docs but i found only read examples
I'm exporting workbook to my local drive through tableau server by using tableau Server Client in python. I'm able to export some workbook but there are some Tableau Workbook which I'm enable to export there comes and error such as 400080: Bad Request
import os
import tableauserverclient as TSC
tableau_auth = TSC.TableauAuth('****', '*****', site_id="")
server = TSC.Server('https://****.net')
server.use_server_version()
tag_to_filter ='Summary'
new_folder_path = 'C:\\Users\\User\\Desktop\\Tableau\\TSC'
with server.auth.sign_in(tableau_auth):
req_option = TSC.RequestOptions().page_size(300)
all_workbooks, pagination_item = server.workbooks.get(req_option)
print([workbook.name for workbook in all_workbooks])
req_option.filter.add(TSC.Filter(TSC.RequestOptions.Field.Name, TSC.RequestOptions.Operator.Equals, tag_to_filter))
for workbook in TSC.Pager(server.workbooks, req_option):
workbook_path = new_folder_path + workbook.name
os.makedirs(workbook_path)
server.workbooks.populate_views(workbook)
for view in workbook.views:
size = TSC.PDFRequestOptions.PageType.Tabloid
orientation = TSC.PDFRequestOptions.Orientation.Landscape
req_option = TSC.PDFRequestOptions(size, orientation)
server.views.populate_pdf(view, req_option)
file_path = workbook_path + '/' + view.name + '.pdf'
print(view.name)
print(file_path)
with open(file_path, 'wb') as image_file:
image_file.write(view.pdf)
print('\tPDF of {0} from {1} workbook'.format(view.name, workbook_path))
raise ServerResponseError.from_response(server_response.content, self.parent_srv.namespace)
tableauserverclient.server.endpoint.exceptions.ServerResponseError:
400080: Bad Request
There was a problem querying the pdf for view 'e91a2103-3347-4c91-8d51-a1ef88321492'.
I have a legacy classic asp application, that allows us to upload pdf files. The file is saved as hex into a MS SQL DB into an image column. Here is a snip of the data in the column:
0x4A564245526930784C6A634E436957317462573144516F784944416762324A7144516F...
We are now working on a rework of the app using node.js as our backend and angular4 as the front end. As part of that, we need to be able to download the same file in the new app. If I upload the same file using angular4 then the image data looks like this:
0x255044462D312E370D0A25B5B5B5B50D0A312030206F626A0D0A3C3C2F547970652F43...
As you can see the hex is completely different and I am not sure what is causing this. I have tried to look at the classic asp code, but its very old and there is no special encoding or such that is happening that would cause this.
In Angular4 we are using the standard input type file control with the change event to capture the contents of the file and then saving it into our db:
myReader.onloadend = (f) => {
let image64;
if (myReader.result.toString()) {
let base64id = ';base64,';
image64 = myReader.result.substr(myReader.result.indexOf(base64id) + base64id.length);
}
someService.upload(image64);
}
So nothing crazy going on, right? The problem now is that I am able to download the file uploaded via angular fine, but not the ones that were uploaded via classic asp. I receive the following error when I try:
Failed to execute 'atob' on 'Window': The string to be decoded is not
correctly encoded.
Here is the code used to upload the files via classic ASP:
Public Function Load()
Dim PosBeg, PosEnd, PosFile, PosBound, boundary, boundaryPos, Pos
Dim Name, Value, FileName, ContentType
Dim UploadControl
PosBeg = 1
PosEnd = InstrB(PosBeg,m_FormRawData,getByteString(chr(13)))
boundary = MidB(m_FormRawData,PosBeg,PosEnd-PosBeg)
boundaryPos = InstrB(1,m_FormRawData,boundary)
'Get all data inside the boundaries
Do until (boundaryPos=InstrB(m_FormRawData,boundary & getByteString("--")))
Set UploadControl = Server.CreateObject("Scripting.Dictionary")
Pos = InstrB(BoundaryPos,m_FormRawData,getByteString("Content-Disposition"))
Pos = InstrB(Pos,m_FormRawData,getByteString("name="))
PosBeg = Pos+6
PosEnd = InstrB(PosBeg,m_FormRawData,getByteString(chr(34)))
Name = getString(MidB(m_FormRawData,PosBeg,PosEnd-PosBeg))
PosFile = InstrB(BoundaryPos,m_FormRawData,getByteString("filename="))
PosBound = InstrB(PosEnd,m_FormRawData,boundary)
If PosFile<>0 AND (PosFile<PosBound) Then
PosBeg = PosFile + 10
PosEnd = InstrB(PosBeg,m_FormRawData,getByteString(chr(34)))
FileName = getString(MidB(m_FormRawData,PosBeg,PosEnd-PosBeg))
UploadControl.Add "FileName", FileName
Pos = InstrB(PosEnd,m_FormRawData,getByteString("Content-Type:"))
PosBeg = Pos+14
PosEnd = InstrB(PosBeg,m_FormRawData,getByteString(chr(13)))
ContentType = getString(MidB(m_FormRawData,PosBeg,PosEnd-PosBeg))
UploadControl.Add "ContentType",ContentType
PosBeg = PosEnd+4
PosEnd = InstrB(PosBeg,m_FormRawData,boundary)-2
Value = MidB(m_FormRawData,PosBeg,PosEnd-PosBeg)
UploadControl.Add "value" , Value
m_dicFileData.Add LCase(name), UploadControl
Else
Pos = InstrB(Pos,m_FormRawData,getByteString(chr(13)))
PosBeg = Pos+4
PosEnd = InstrB(PosBeg,m_FormRawData,boundary)-2
Value = getString(MidB(m_FormRawData,PosBeg,PosEnd-PosBeg))
UploadControl.Add "value" , Value
End If
m_dicForm.Add LCase(name), UploadControl
BoundaryPos=InstrB(BoundaryPos+LenB(boundary),m_FormRawData,boundary)
Loop
Load = m_blnSucceed
End Function
Any idea what I can do here to fix this? I need to be able to download the old files as well. Is there an encoding or something else I am missing here?
After further looking into this, it seems the following is causing the error: Node.js will send a buffer to the front-end. On the front-end I then convert the buffer into base64 using the below code:
_arrayBufferToBase64(buffer) {
let base64 = '';
let bytes = new Uint8Array(buffer);
let len = bytes.byteLength;
for (let i = 0; i < len; i++) {
bbase64 += String.fromCharCode(bytes[i]);
}
return base64;
}
The returned base64 from the old classic asp is returned as garbage and looks like this:
*R{£ÌõGpÃì#j¾é>i¿ê A l Ä ð!!H!u!¡!Î!û"'"U""¯"Ý#
(?(q(¢(Ô))8)k))Ð5*hÏ++6+i++Ñ,,9,n,¢,×-''I'z'«'Ü(
3F33¸3ñ4+4e44Ø55M55Â5ý676r6®6é7$7`77×88P88È99B99¼9ù:6:t
I am still not sure how to fix this though.
I'm trying to do dynamic testing where I query the database, use Groovy to loop through and gather the data I need, and execute the validation web service with the results from the database call.
Here is my code:
response = context.expand('${JDBC Get User Task#ResponseAsXml}')
String userTaskId
int r = 1
def root = new XmlSlurper().parseText(response)
tc = testRunner.testCase
ts = tc.testSteps["Get usertask by id"]
root.ResultSet.Row.each{ row ->
log.info "Data for row: " + r.toString()
userTaskId = row["USER_TASKS.USER_TASK_ID"]
tc.setPropertyValue("result", userTaskId)
ts.run(testRunner, context)
r++
}
The problem I'm having is that if 3 results are returned, the only one that gets executed is the 3rd time into the validation web service.
I need to access the signing appliance from Python. To this end, using suds, I generated a Python client from the WSDL at https://cosigndemo.arx.com:8080/sapiws/dss.asmx?wsdl .
I then used the generated client to build a simple signature request which more or less does the same as shown in the Java example provided by ARX (only, I'm asking for an invisible signature).
The problem is that when I send the request to the demo appliance, here's what I receive in return:
(DssSignResult){
Result =
(Result){
ResultMajor = "urn:oasis:names:tc:dss:1.0:resultmajor:ResponderError"
ResultMinor = "urn:oasis:names:tc:dss:1.0:resultminor:GeneralError"
ResultMessage =
(ResultMessage){
value = "Exception occured"
_lang = "en"
}
}
}
Here's the Python code I wrote:
from suds.client import Client
from suds.plugin import MessagePlugin
from suds.sax.attribute import Attribute
import logging
class MyPlugin(MessagePlugin):
def marshalled(self, context):
foo = context.envelope.getChild('ns2:Body').getChild('ns0:DssSign').getChild('ns0:SignRequest').getChild('ns1:InputDocuments').getChild('ns1:Document')
foo[0].attributes.append(Attribute('MimeType', 'application/pdf'))
print context.envelope
url = 'https://cosigndemo.arx.com:8080/sapiws/dss.asmx?wsdl'
client = Client(url, plugins=[MyPlugin()])
cid = client.factory.create("ns4:ClaimedIdentity")
cid.Name = "John Miller"
cid.SupportingInfo.LogonPassword = "12345678"
sigReq = client.factory.create("ns4:RequestBaseType")
sigReq._RequestID = "DummyRequestId"
sigReq.OptionalInputs.ClaimedIdentity = cid
sigReq.OptionalInputs.SignatureType="http://arx.com/SAPIWS/DSS/1.0/signature-field-create-sign"
sigReq.OptionalInputs.SAPISigFieldSettings._Name = "SigField"
sigReq.OptionalInputs.SAPISigFieldSettings._Invisible = "true"
sigReq.OptionalInputs.SAPISigFieldSettings._DependencyMode = "Independent"
sigReq.OptionalInputs.SAPISigFieldSettings._SignatureType = "Digital"
sigReq.OptionalInputs.SAPISigFieldSettings._EmptyFieldLabel = ""
sigReq.OptionalInputs.SAPISigFieldSettings._Page = "1"
sigReq.OptionalInputs.ReturnPDFTailOnly = "true"
sigReq.OptionalInputs.IncludeObject=None
sigReq.OptionalInputs.SignaturePlacement=None
doc = client.factory.create("ns4:DocumentType")
doc.Base64Data = open("/Users/mar/CoSignWSDL/factures-comptat217.pdf", "rb").read().encode("base64")
sigReq.InputDocuments.Document = doc
result = client.service.DssSign(sigReq)
print result
And here's the SOAP that (at least I think) it is sending to the appliance:
<SOAP-ENV:Envelope xmlns:ns3="http://arx.com/SAPIWS/DSS/1.0" xmlns:ns0="http://arx.com/SAPIWS/DSS/1.0/" xmlns:ns1="urn:oasis:names:tc:dss:1.0:core:schema" xmlns:ns2="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header/>
<ns2:Body>
<ns0:DssSign>
<ns0:SignRequest RequestID="DummyRequestId">
<ns1:OptionalInputs>
<ns1:ClaimedIdentity>
<ns1:Name>John Miller</ns1:Name>
<ns1:SupportingInfo>
<ns3:LogonPassword>12345678</ns3:LogonPassword>
</ns1:SupportingInfo>
</ns1:ClaimedIdentity>
<ns1:SignatureType>http://arx.com/SAPIWS/DSS/1.0/signature-field-create-sign</ns1:SignatureType>
<ns1:SAPISigFieldSettings Name="SigField" DependencyMode="Independent" SignatureType="Digital" Page="1" Invisible="true"/>
<ns1:ReturnPDFTailOnly>true</ns1:ReturnPDFTailOnly>
</ns1:OptionalInputs>
<ns1:InputDocuments>
<ns1:Document>
<ns1:Base64Data MimeType="application/pdf">...</ns1:Base64Data>
</ns1:Document>
</ns1:InputDocuments>
</ns0:SignRequest>
</ns0:DssSign>
</ns2:Body>
</SOAP-ENV:Envelope>
Note that I replaced the (very long) Base64Data content with "..." here for space reasons.
Why is it not working?
Update Problem solved thanks to the answer. Here is the working code that adds a sig to a PDF:
from suds.client import Client
from suds.plugin import MessagePlugin
from suds.sax.attribute import Attribute
from suds.bindings import binding
import logging
import xml.dom.minidom as minidom
import base64
class MyPlugin(MessagePlugin):
def marshalled(self, context):
documentNode = context.envelope.getChild('ns3:Body').getChild('ns0:DssSign').getChild('ns0:SignRequest').getChild('ns1:InputDocuments').getChild('ns1:Document')
documentNode[0].attributes.append(Attribute('MimeType', 'application/pdf'))
SAPISigFieldSettingsNode = context.envelope.getChild('ns3:Body').getChild('ns0:DssSign').getChild('ns0:SignRequest').getChild('ns1:OptionalInputs').getChild('ns1:SAPISigFieldSettings')
SAPISigFieldSettingsNode.setPrefix('ns2')
ReturnPDFTailOnlyNode = context.envelope.getChild('ns3:Body').getChild('ns0:DssSign').getChild('ns0:SignRequest').getChild('ns1:OptionalInputs').getChild('ns1:ReturnPDFTailOnly')
ReturnPDFTailOnlyNode.setPrefix('ns2')
signRequestNode = context.envelope.getChild('ns3:Body').getChild('ns0:DssSign').getChild('ns0:SignRequest')
signRequestNode.setPrefix('ns1')
binding.envns = ('SOAP-ENV', 'http://www.w3.org/2003/05/soap-envelope')
url = 'https://cosigndemo.arx.com:8080/sapiws/dss.asmx?wsdl'
client = Client(url, plugins=[MyPlugin()])
logging.basicConfig(level=logging.INFO)
logging.getLogger('suds.plugin').setLevel(logging.DEBUG)
cid = client.factory.create("ns4:ClaimedIdentity")
cid.Name = "John Miller"
cid.SupportingInfo.LogonPassword = "12345678"
sigReq = client.factory.create("ns4:RequestBaseType")
sigReq._RequestID = "DummyRequestId"
sigReq.OptionalInputs.ClaimedIdentity = cid
sigReq.OptionalInputs.SignatureType="http://arx.com/SAPIWS/DSS/1.0/signature-field-create-sign"
sigReq.OptionalInputs.SAPISigFieldSettings._Name = "SigField"
sigReq.OptionalInputs.SAPISigFieldSettings._Invisible = "true"
sigReq.OptionalInputs.SAPISigFieldSettings._DependencyMode = "Independent"
sigReq.OptionalInputs.SAPISigFieldSettings._SignatureType = "Digital"
sigReq.OptionalInputs.SAPISigFieldSettings._EmptyFieldLabel = ""
sigReq.OptionalInputs.SAPISigFieldSettings._Page = "1"
sigReq.OptionalInputs.ReturnPDFTailOnly = "true"
sigReq.OptionalInputs.IncludeObject=None
sigReq.OptionalInputs.SignaturePlacement=None
doc = client.factory.create("ns4:DocumentType")
f = open('/Users/mar/CoSignWSDL/factures-comptat217.pdf', 'r+b')
doc.Base64Data = f.read().encode("base64")
sigReq.InputDocuments.Document = doc
result = client.service.DssSign(sigReq)
signature = base64.b64decode(result.SignatureObject.Base64Signature.value)
f.seek(0, 2) #go to the end of the file
f.write(signature)
f.close()
Your SOAP request is passing ns0:SignRequest Should be: ns1:SignRequest
Also Bear in mind that there is a missing part in your code--checking the appliance's SSL certificate. It is not a must, but generally required in order to verify that the response wasn't sent by a fraudulent entity.
In .Net and Java it is done automatically when accessing web service in HTTPS (based on Microsoft certificate Store and Java certificate Store)- but as far as I see there is no equivalent in Python.