OK, as promised, this is the current method I’ve built for posting and downloading from the extension signing form, as part of an automated build process we use to put our plugins together for all our platforms at once. In the end, I figured out how to get rid of Selenium
or any other headless browser and do it all with Requests
.
I haven’t removed our logging from this method—you should definitely remove the g_logger
calls from the try/except blocks and add whatever you want there, or you’ll get errors on the undeclared g_logger
.
Also, you’ll need to pass in your logged in session from the previous method (posted above) in order to make this work.
(And, of course, this is somewhat subject to break if Trimble makes changes to the signing form!)
In the future I’ll be updating this to work with urllib2
instead of Requests
, if an API doesn’t make the whole thing obsolete.
This assumes you’ve already imported a few modules like re
, requests
, lxml
, and so on.
def post_rbz(rbz_file, session):
"""Use our signed-in session to send POST requests to SketchUp's extension
signature form. There are two POST requests required to to make this work:
POST 1: upload the .rbz file to the plupload upload handler, using a token.
POST 2: submit the .rbz signing form, using the form tokens and references
to the uploaded file.
After posting, get the download link, and then pass it back to our session
to download it.
Return the signed file.
"""
url = 'https://extensions.sketchup.com/en/developer_center/extension_signature'
try:
# Get the page so we can extract the plupload_token
form_page = session.get(url)
# Use a regex to find the plupload token value embedded in the plupload script
plupload_token = re.search('plupload_token=(.+?)"',
form_page.content).group(1)
files = {('file', open(rbz_file, 'rb'))}
# We must provide a name for the tempfile once it's uploaded, and then
# refer to it by name when we submit the signing form.
data = {'name': 'tempfile.rbz'}
# # DEBUG: use this hook callback function to check the POST response.
# # To use this, add this line to the POST request:
# # hooks={'response': print_content}
# def print_content(r, *args, **kwargs):
# print(r.content)
# The file uploaded must be posted to the pluploader url.
plup_url = 'https://extensions.sketchup.com/en/plupload-handle-uploads'
g_logger.debug('Posting the .rbz file for upload with plupload token {}...'.format(plupload_token))
res = session.post(
plup_url,
files=files,
data=data,
params={"plupload_token": plupload_token}
)
except Exception as e:
g_logger.error('Unable to post to uploader: {}'.format(e))
raise e
try:
# Submit the signing form, with references to our uploaded file.
# First, get the signing page so we can grab required tokens and form data values:
sign = session.get('https://extensions.sketchup.com/en/developer_center/extension_signature')
sign_html = lxml.html.fromstring(sign.text)
hidden_inputs = sign_html.xpath(r'//form[@id="ruby-certificate-upload-form"]//input[@type="hidden"]')
# Add data values relating to the uploaded file
form_data = {x.attrib["name"]: x.value for x in hidden_inputs}
form_data['op'] = 'Sign+the+Extension'
form_data['edit-file_count'] = '1'
form_data['edit-file_0_status'] = 'done'
# This sets the file name for the download file:
form_data['edit-file_0_name'] = '<our actual plugin name>.rbz'
# This sets the reference to the temp file we uploaded:
form_data['edit-file_0_tmpname'] = 'tempfile.rbz'
g_logger.debug('Posting to submit the .rbz signing form with form data {}...'.format(form_data.items()))
# Submit the form with the data.
res = session.post(
url,
data=form_data
)
except Exception as e:
g_logger.error('Unable to post to .rbz signing form: {}'.format(e))
raise e
# Find the download URL for the signed extension by parsing the response html
# Use xpath to find the message download link containing our extension file name.
try:
g_logger.debug('Searching for the download link in the response html...')
res_html = lxml.html.fromstring(res.content)
dl_msg_el = res_html.xpath('//*[@class="messages status"]/a[@href[contains(., "<our actual plugin name>.rbz")]]')[0]
dl_url = dl_msg_el.get('href')
g_logger.debug('Download link found: {}.'.format(dl_url))
except Exception as e:
g_logger.error('Unable to get a signed extension: {}'.format(e))
raise e
try:
# Use the session to download the signed .rbz extension.
temp_dir = os.path.dirname(rbz_file)
dl_rbz = os.path.expanduser(os.path.join(temp_dir, '<our actual plugin name>.rbz'))
g_logger.info('Downloading the signed .rbz to {}...'.format(dl_rbz))
res = session.get(dl_url, stream=True)
with open(dl_rbz, 'wb') as f:
shutil.copyfileobj(res.raw, f)
g_logger.info('Signed .rbz downloaded successfully.')
return dl_rbz
except Exception as e:
g_logger.error('Unable to download signed extension: {}'.format(e))
raise e