2012/04/02

Building a PHPSuite while napping (pt. 2)


In the last post I discussed writing a general purpose restlet to allow scripting a Netsuite search in PHP. In this post we'll cover how PHP talks to Netsuite's restlet service, and in my next post (hopefully sooner than later) we'll cover implementing a search in PHP.


To start off we need to register our script from last time with Netsuite. Create a new Suitescript Restlet:







Upload the script as a new file and add our GET function to the respective RESTlet hook. Note that there are empty functions in our file for the remaining hooks: they can be assigned now as well, or left empty for later addition. Don't forget that our script has dependencies on classes for base64 and json. These can be injected directly into the file, but I prefer to include them as demonstrated here.



Now we're ready to deploy. RESTlet access has four control points: netsuite acct number, role, username, and password of an existing user account. Ideally you will setup one or more users exclusively for this access. While developing (hopefully in a sandbox environment) I prefer to limit access to users with Full Access privileges. YMMV. Set to Released status off-the-bat, but keep the Log level set to Audit so we can customize between low-priority and high-priority logging.

Now that our script is in place we're ready to dig into PHP. The curl library offers nearly everything we need to access our RESTlet, but to keep things easy down the road it needs a little improvement.

Create a new class like so

class NsRestRequest
{
protected $url;
protected $verb;
protected $request;
protected $response;
protected $headers;

public function __construct( $url=null, $verb='GET', $data=null )
{
$this->verb = $verb;
$this->request = $data;
$this->url = $url;
$this->response = null;
$this->headers = array();

if ($this->request !== null)
{
$this->buildPostBody();
}
}

public function buildPostBody ($data = null)
{
$data = ($data !== null) ? $data : $this->request;
if (!is_object( $data ) && !is_array( $data ))
{
throw new Exception( 'Invalid data input for postBody ['.gettype( $data ).']' );
}
$data = json_encode( $data );
$this->request = $data;
}
}

This is a good start for our REST class - we've got a lot of room for growth, but it doesn't actually do anything yet. Our first bit of functionality is here:

public function setNsHeaders()
{
$this->headers = array(
'Content-Type: application/json',
'Authorization: NLAuth nlauth_account=ACCTXXX,nlauth_email=restuser@netsuite.com,nlauth_signature=xxxxxx,nlauth_role=21,
);
}
Getting the http-headers setup properly for Netsuites approval is the hardest part of this entire process. Did you make it through ok? Just fill in your own info here and you'll be all set. (The astute will note that we are preparing to send these authorization headers with every single request. Netsuite doesn't seem to have a way to establish any sort of session, which is a shame since passing a static auth-header makes the system susceptible to replay attacks and makes the request bulkier than it needs to be. There was talk of simulating a session in Netsuite for SOAP requests and is something that ought to be pursued down the road.)

Now a simple execution chain

public function execute () {
$this->setNsHeaders();
$ch = curl_init();
try {
switch (strtoupper( $this->verb )) {
case 'GET':
$this->executeGet($ch);
break;
default:
throw new InvalidArgumentException('"' . $this->verb . '" is an invalid REST verb.');
}
curl_close( $ch );
}
catch (Exception $e)
{
curl_close( $ch );
throw $e;
}
}

protected function executeGet( $ch )
{
$this->doExecute( $ch );
}

protected function doExecute( &$ch )
{
curl_setopt( $ch, CURLOPT_TIMEOUT, 0 );
curl_setopt( $ch, CURLOPT_URL, $this->url );
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, false );
curl_setopt( $ch, CURLOPT_HTTPHEADER, $this->headers );

$this->response['body'] = curl_exec( $ch );
$this->response['info'] = curl_getinfo( $ch );

if ($this->response['body'] == false)
{
throw new Exception( curl_error( $ch ) );
}
}
A couple of things to note here. First we setup a method, execute(), as a general-purpose method that will figure out what work to do for us based on the verb. When we decide to extend the class we can simply add executePost(), executePut(), etc. methods and then add them to the switch logic. For now we keep things simple with just executeGet(), which is nothing more than a pass-thru to the doExecute method. While we could drop executeGet() without any trouble, it is useful to setup a proper pattern for later usage.

doExecute() is where the magic happens: we setup the CURL headers, execute the request and store the results and transaction info in response. Two points of interest, make sure the returntransfer switch is set to true so you can process the response, rather than losing it to a direct output. ssl_verifypeer should be set to true for security purposes. (I had a devil of a time making requests from the sandbox until I realized that CURL was attempting to verify a self-signed certificate and failing. The better route would be to setup a directory for alternate certificates; since the production certificates are valid, I prefer to keep things loose in development, just can't forget to enable the switch when migrating to a live environment.)

We now have a fully functional REST class that will carry all of Netsuites baggage for us. We'll put it to work in the next post.

2 comments:

  1. 1. Great article series.
    2. The embedded images are illegible, and don't seem to link to bigger versions.

    ReplyDelete
  2. Hi Matthew,
    Do you happen to have larger versions of these images?
    Thanks for the post, by the way. This is exactly what I have been looking for.
    -Keith

    ReplyDelete