NetSuite RESTlet Example (OAuth1): Python Script To RESTlet

How do you send data to your NetSuite RESTlet using Python?

To send data to your NetSuite RESTlet using Python code utilise the handy requests_oauthlib library.

If you need to upload a series of CSV files and want to perform some data cleaning before being uploaded and want to avoid the Import CSV process then you can create a simple RESTlet and insert your clean data directly.

Create Your RESTlet

First, create your RESTlet and make it perform the basic tasks needed to import the data you want.

Keep your RESTlet simple and have it return either errors for improper format, insufficient parameters or IDs of newly created records. Put the emphasis back on the request to have everything you need to create a new record.

I find separating out into distinct modules the scripts that will perform the insertion of data into your records.

A simple RESTlet and the would therefore look something like this:

/**
 * @NApiVersion 2.1
 * @NScriptType Restlet
 */
define(['N/error'],
/**
 * @params {error} error
 */
(error) => {
  const doValidation = (props, propNames) => {
    const errors = props.reduce(
      (accum, prop, idx) => {
        if (!prop && prop !== 0) accum.push(`Missing a required argument: ${propNames[idx]}`);
        return accum;
      }
      , []
    );
    if (errors.length) throw error.create({ name: 'MISSING_REQUIRED_ARGS', message: errors.join("\n") });
  }
  const post = (params) => {
    log.debug({title: 'POST request (params)', details: params});
    doValidation([params.haveThis], ['haveThis']);
    // check parameters are correct data type, or perform type conversions (if needed)
    const haveThis = parseInt(params["haveThis"]) || 0;
    // send data through
    createMyRecords({haveThis});
  }
  return {post}
}
);

As you can see from the above code I try to keep the footprint of my RESTlet code light. In essence, I just want to check I have the needed parameter values by using a doValidation function similar to what Netsuite have in their documents, but I want to collect ALL errors with the validation process and not just throw at EACH error which the standard docs have.

However, the very first thing I tend to write with any RESTlet function ( get , post , put , doDelete ) is the debug log of the parameters coming through. You’ll want to check these values when you first write your Python code when you send your first request. By writing this code first you are diagnosing whether your connection by your external code to the RESTlet passes authentication.

Next, you’ll want to perform all your necessary checks on the params object being passed in (the sole argument to the REST function).

After checking the params object I will then perform some basic data type conversions if needed. One popular type conversion I do is for dates and I would import the N/format library to help with processing these types of variables.

This would look something like this if importing the N/format library:

/**
 * @NApiVersion 2.1
 * @NScriptType Restlet
 */
define(['N/error', 'N/format'],
/**
 * @params {error} error
 * @params {format} format
 */
(error, format) => {
  const doValidation = (props, propNames) => {
    const errors = props.reduce(
      (accum, prop, idx) => {
        if (!prop && prop !== 0) accum.push(`Missing a required argument: ${propNames[idx]}`);
        return accum;
      }
      , []
    );
    if (errors.length) throw error.create({ name: 'MISSING_REQUIRED_ARGS', message: errors.join("\n") });
  }
  const post = (params) => {
    log.debug({title: 'POST request (params)', details: params});
    doValidation([params.haveThis], ['haveThis']);
    // check parameters are correct data type, or perform type conversions (if needed)
    const haveThis = format.parse({value: parse["haveThis"], type: format.Type.DATE});
    // send data through
    createMyRecords({haveThis});
  }
  return {post}
}
);

Once all the checks and types are performed I then send through the data to be processed according to the parameters of the function being passed the information.

I find having my RESTlet code separated from the rest of my code helps to show where problems lie. If you’re having connection issues then the debugging log will not be showing any parameters being received. If you’re having data entry problems then it could be the data being sent through to the processing modules.

Connecting To RESTlet From Python

Once you have uploaded your script to NetSuite and created a RESTlet script record you will need to deploy your script so that you can send your request to an HTTPS endpoint.

Once you’ve created a deployment you will have a URL that looks something like this:

https://{REALM_ID}.restlets.api.netsuite.com/app/site/hosting/restlet.nl?script={SCRIPT_ID}&deploy={DEPLOY_ID}

Some components of the URL will be unique to your script and deployment.

Once you have the URL you need to fetch the integration key and secret along with the user access ID and secret. This means you will have 4 hashes in your hand each of which are needed to connect to the endpoint.

The variables CLIENT_ID and CLIENT_SECRET are the integration key and secrets; whereas the Access Tokens received for the user interacting with the integration are the variables ACCESS_KEY and ACCESS_SECRET in the code below.

Once you have these five elements you can jump over to your Python code and test you can connect based on these.

Here’s a quick snippet of Python code to run to check:

from requests_oauthlib import OAuth1Session
import json


CLIENT_KEY: str = "HASH"
CLIENT_SECRET: str = "HASH"
ACCESS_KEY: str = "HASH"
ACCESS_SECRET: str = "HASH"
SIGNATURE_METHOD: str = "HMAC-SHA256"
REALM_ID: str = "1234567"
SCRIPT_ID: int = 1
DEPLOY_ID: int = 1
URL: str = f"https://{REALM_ID}.restlets.api.netsuite.com/app/site/hosting/restlet.nl?script={SCRIPT_ID}&deploy={DEPLOY_ID}

oauth = OAuth1Session(
    client_key=CLIENT_KEY,
    client_secret=CLIENT_SECRET,
    resource_owner_key=ACCESS_KEY,
    resource_owner_secret=ACCESS_SECRET,
    realm=REALM_ID
    signature_method=SIGNATURE_METHOD
)

data = {"Suite": "Script"}

headers = {
    "Content-Type": "application/json"
}

res = oauth.post(URL, data=json.dumps(data), headers=headers)
print(res)owner)

The purpose of this simple Python code is to test your credentials to see you have properly pasted the correct elements into the right variables.

There are a couple of things to be mindful of with this code which can lead to problems if they are taken out.

Firstly, the signature_method must be set to HMAC-SHA256 . See here for more detail .

Secondly, a header object needs to be inserted with "Content-Type": "application/json" if you want your RESTlet code to handle a JSON object with the data being sent through, otherwise without this header property set the data will be sent through as a string. Note also that header properties are CASE SENSITIVE , therefore if you’re sending through "content-type": "application/json" it will not be recognised.

If you are getting a 403 error then check you have the correct URL as well as the correct credentials. Double check them again. Don’t write any additional code, you just want to check the connection is good.

Once you get a 200 response you then want to go over to your RESTlet script record and check there has been a debug log of the data contained in your Python data variable that was passed through.

Summary

Sending data from a Python script to a Netsuite RESTlet is an easy task thanks to the requests_oauthlib library which enables users to create a session and making multiple requests to the RESTlet endpoint without having to authenticate at each request submission.

Photo of author
Ryan Sheehy
Ryan has been dabbling in code since the late '90s when he cut his teeth exploring VBA in Excel. Having his eyes opened with the potential of automating repetitive tasks, he expanded to Python and then moved over to scripting languages such as HTML, CSS, Javascript and PHP.