I'm working on building a Python class (or classes) that implement the XSI-Actions and XSI-Events API's. I'm getting reasonable resposnes against the Sandbox for some of the Actions, but I'm getting Internal Server Errors when I try to register for an Events Channel and trying to work with async notifications. The 500 Internal Server Errors are only cropping up in the Sandbox envrionment - the code I'll be posting (at least with respect to the creation of Events Channels) works against the deployed version from our phone provider. Their system, however, has other problems (namely, I can't look up created Channels or reference my ChannelSet at present) so they're looking into those issues for me. I'd presume (however naively) that the code I'm writing ought to work against the Sandbox and against the live system, so I'm posting it here so I can try to get some additional assistance and am not unproductive while waiting for my provider to fix their deployment.
As all of this API is being retrieved over HTTP, the classes need to start there.In Python we end up with something like this as a starting basis:
class EventsChannel(object):
def Connection(self, host, port = 443):
Klass = httplib.HTTPConnection if port == 80 else httplib.HTTPSConnection
debug("Connecting to %s on %d" % (host, port))
conn = Klass(host, port)
conn.request('HEAD', '', headers = EventsChannel.headers)
res = conn.getresponse()
debug('Status: %d Reason: %s' % (res.status, res.reason))
if res.status in (301, 302):
debug("Redirecting to %s" % res.getheader('location'))
newhost = proto.sub('', res.getheader('location').rstrip('/'))
conn = self.Connection(*urllib.splitnport(newhost, port))
if res.status in (200,):
conn = Klass(host, port)
return conn
This method allows me to request connections, preferably secure, to a server. There's some extra work involved to handle redirects, as well as a final re-creation of the connection that seemed necessary only when connecting to the Sandbox. I actually think that's related to the error I get a few calls later, when I do this:
def make_conduit(self):
# Build up the XML Content for the Event Conduit.
XML = Document()
channel = XML.createElement('Channel')
channel.setAttribute('xmlns', schema)
props = [('channelSetId', self.csid),
('priority', '1'),
('weight', '50'),
('expires', '3600')]
for name, val in props:
elem = XML.createElement(name)
elem.appendChild(XML.createTextNode(val))
channel.appendChild(elem)
XML.appendChild(channel)
body = XML.toxml()
print XML.toprettyxml()
# Create the connection and POST to open the channel.
return self.post(self.url(async, events, version, 'channel'), body)
def post(self, url, body, connection = None):
if connection is None:
connection = self.Connection(baseurl)
headers = EventsChannel.headers.copy()
headers['Content-Length'] = '%d' % len(body)
connection.request('POST', url, '', headers)
connection.send(body)
return connection.getresponse()
The methods here are responsible for building the XML Body that will be posted to create an Events Channel and return an HTTPResponse that I can read events from. This code worked against my Provider's deployment, but does not work against the Sandbox. The XML Content that gets POSTed looks like this when printed with reasonable formatting:
<?xml version="1.0" ?>
<Channel xmlns="http://schema.broadsoft.com/xsi">
<channelSetId>c464e68f-6714-11e2-81d1-08002700c0e6</channelSetId>
<priority>1</priority>
<weight>50</weight>
<expires>3600</expires>
</Channel>
That's generated as per the API Documentation I can find. Posting that content to the Sandbox produces a 500 Internal Server Error.
The only other method I think that's called in this demo code is this one:
def url(self, *args):
return '/'.join(itertools.chain([''], args))
And that's just a convenience method for putting together urls. I've defined the following constants as well:
async = 'com.broadsoft.async'
actions = 'com.broadsoft.xsi-actions'
events = 'com.broadsoft.xsi-events'
schema = 'http://schema.broadsoft.com/xsi'
version = 'v2.0'
proto = re.compile('^https?://')
# These values for Sandbox Environment.
baseurl = 'xsp2.xdp.broadsoft.com'
user = >>myuser<<
pwd = >>mypwd<<
Those constants make for easy generation of dynamic urls. Finally, as a way to test that I'm accessing the API even close to correctly, I added methods to retrieve a user profile:
def get(self, url):
C = self.Connection(baseurl)
C.request('GET', url, '', EventsChannel.headers)
return C.getresponse()
def getuserprofile(self, userid):
return self.get(self.url(actions, version, 'user', userid, 'profile'))
Those seem to work, and provide me the following JSON Response:
{u'Profile': {u'@xmlns': {u'$': u'http://schema.broadsoft.com/xsi'},
u'additionalDetails': {},
u'countryCode': {u'$': u'1'},
u'details': {u'extension': {u'$': u'8220'},
u'firstName': {u'$': u'gddc_brooksnet'},
u'groupId': {u'$': u'gddc_brooksnetGroup'},
u'lastName': {u'$': u'User1'},
u'number': {u'$': u'2401008220'},
u'serviceProvider': {u'$': u'gddc_brooksnetEnt',
u'@isEnterprise': u'true'},
u'userId': {u'$': u'gddc_brooksnetUser1@xdp.broadsoft.com'}},
u'fac': {u'$': u'/v2.0/user/gddc_brooksnetUser1@xdp.broadsoft.com/profile/Fac'},
u'passwordExpiresDays': {u'$': u'2147483647'},
u'portalPasswordChange': {u'$': u'/v2.0/user/gddc_brooksnetUser1@xdp.broadsoft.com/profile/Password/Portal'},
u'registrations': {u'$': u'/v2.0/user/gddc_brooksnetUser1@xdp.broadsoft.com/profile/Registrations'},
u'scheduleList': {u'$': u'/v2.0/user/gddc_brooksnetUser1@xdp.broadsoft.com/profile/Schedule'}}}
I take the success of the simple GET Requests (along with the almost success against my providers servers) to mean that I'm close, but again, can't test further because of the 500 Internal Server Error I get when attempting to create the Events Channel on the Sandbox Server.