NetSuite: Publish portlet to Customer Center with browser automation
November 24, 2014
As previously mentioned, I’ve been consluting with a client on integrating and setting up some of the functionality they require with their new NetSuite deployment. As with almost all SAAS solutions, NetSuite excels within it’s core competancy, but can cause issues when trying to achieve functionality outside of this.
This is how we were able to implement browser automation to perform the automated setup of thousands of customer centers with which would otherwise have taken weeks to do by hand and would have likely resulted in a higher rate of implementation errors.
Background
The goal was to allow users to access account-specific information on my clients’s website using one login managed through NetSuite. The best solution ended up using the built-in NetSuite outbound SSO to generate a unique link within NetSuite, and use that link to authenticate against in our WordPress installation.
We setup a custom portlet within NetSuite that handled all of the link generation and display, and the logic within wordpress to accept the verification data, and perform the handshake with NetSuite to pull the customer information.
Unfortunately, once this was done, we found out the only method to publish this portlet to the thousands of customer centers that the users access, was to manually do it within a browser.
Below is the python script we wrote using the selenium library to automate a browser instance and do this for us.
Code
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*
3
4 from selenium import webdriver
5 from selenium.webdriver.common.keys import Keys
6 from selenium.webdriver.support.ui import WebDriverWait
7 from selenium.webdriver.support import expected_conditions as ec
8 from selenium.webdriver.common.by import By
9 from selenium.common.exceptions import NoSuchElementException
10 import time
11 import csv
12 import logging
13 import datetime
14
15 #setup log file for review later
16 logger = logging.getLogger('setup-portlet')
17 hdlr = logging.FileHandler('log'+datetime.datetime.now().strftime("%Y%m%d%H%M%S")+'.log')
18 formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
19 hdlr.setFormatter(formatter)
20 logger.addHandler(hdlr)
21 logger.setLevel(logging.INFO)
22
23 #open browser instance
24 browser = webdriver.Firefox()
25 browser.implicitly_wait(1)
26
27 csv_file = 'access-info.csv'
28
29 #function that takes browser instance along with email and password
30 #adds portlet to the customer center or checks for existing porlet
31 def update_user(browser, email, password):
32
33 #open login page and fill out form
34 browser.get('http://system.netsuite.com')
35
36 elem = browser.find_element_by_name('email')
37 elem.send_keys(email)
38
39 elem = browser.find_element_by_name('password')
40 elem.send_keys(password + Keys.RETURN)
41
42 #navigate to correct tab
43 elem = browser.find_element_by_link_text('Screen Calculator')
44 elem.click()
45
46 #open setup dialog
47 try:
48 elem = browser.find_elements_by_xpath("html/body/div[1]/div[2]/div/div[2]/div/div[5]/div[1]/div/div[2]/div/div[1]/p/span")[0]
49 elem.click()
50 except Exception:
51 #try to find portlet link, if found exit function, otherwise raise error
52 try:
53 elem = browser.find_element_by_link_text('Access Screen Calculator')
54 except Exception:
55 raise
56 else:
57 print " portlet already setup"
58 logger.info("portlet already setup")
59 pass
60 return
61 raise
62
63 #switch to iframe
64 WebDriverWait(browser, 10).until(
65 ec.frame_to_be_available_and_switch_to_it("scriptportlet_setupDialog_frame")
66 )
67
68 WebDriverWait(browser, 10).until(
69 ec.presence_of_element_located((By.CLASS_NAME, "ext-gecko"))
70 )
71
72 #wait for javascript to load
73 time.sleep(1) #seconds
74
75 #select dropdown
76 elem = browser.find_element_by_id("inpt_scriptsource1")
77 elem.click()
78
79 #select Screen Calculator option
80 elem = browser.find_elements_by_xpath("html/body/div[2]/div/div/div[2]")[0]
81 elem.click()
82
83 #save form
84 elem = browser.find_element_by_id("submitter")
85 elem.click()
86
87 print " portlet successfully setup!"
88 logger.info("portlet successfully setup!")
89
90 #switch back to main window
91 #browser.switch_to.default_content
92
93 #sign out
94 #elem = browser.find_elements_by_xpath("html/body/div[1]/div[1]/div[1]/div[8]/div/ul/li[1]/a")[0]
95 #elem.click()
96
97 with open(csv_file, 'rb') as csvfile:
98 #open csv file with access information
99 access_file = csv.reader(csvfile, delimiter=',')
100 next(access_file) # skip the first line
101 row_counter = 1
102 for row in access_file:
103 email = row[3] #pull access infomration
104 password = row[8]
105 internalid = row[0]
106 access = row[7]
107 if access == "No" or not password or not email: # if information is missing, skip
108 print "Skipping: "+email+","+password+" information missing / access disabled"
109 continue # skip if no access
110 print str(row_counter)+") Attempting: "+email+","+password
111 logger.info(str(row_counter)+") Attempting: "+email+","+password)
112 row_counter += 1
113
114 attempts = 0
115 while attempts < 5: # on error, attempt 5 times, then fail out
116 try:
117 update_user(browser, email, password)
118 break
119 except Exception:
120 attempts += 1
121 if attempts is 5:
122 print " permanent fail"
123 logger.error('permanent fail')
124 else:
125 print " unsuccessful attempt"
126 logger.error('unsuccessful attempt')