How to troubleshoot 'Invalid Page Parameter' error in Netsuite - netsuite

I'm still relatively new to NetSuite, and have taken over a project that a contractor did for us, so this may be a simple question. We have a Suitelet that is calling another Suitelet with nlapiRequestURL. The Suitelet that is being called, is supposed to return a single ID. Instead, we get a 'Invalid Page Parameter' response.
I looked at the configuration for the Suitelet that is called, and there is a parameter setup, but the 'Preference' field on that parameter is blank, which according to the documentation suggests that when that is the case, the parameter value should be defined in the Deployment, on the Parameter tab. Which it is, and appears to be the correct value.
So I assume the issue is with one of the parameters in the URL that is being used in nlapiRequestURL, and not the parameter defined on the script record in Netsuite. Here is an example of one of those URLs. I just changed the compid for obvious reasons:
https://forms.sandbox.netsuite.com/app/site/hosting/scriptlet.nl?script=147&deploy=1&compid=12345&h=e06792b0e7727d53f816&internalIds=17509
I verified that '147' is the correct script id. I also tried removing each of the parameters one by one from the URL, but that doesn't work. Depending on which one I remove, I either get 'An unexpected error occurred', or 'Missing a required parameter'. The only parameter that is being used inside the Suitelet that is called is 'internalids'. I can see that it's being obtained with 'request.getParameter("internalIds")'. (See code below)
As a sidenote, I'm not sure what the 'h' parameter is for in the URL, and can't find where that, or compid, or deploy is configured. I'm wondering if that 'h' value is incorrect, but am not sure what it's for. Why are 'compid', 'deploy' and 'h' being included in the URL? I can assume the compid is for identifying our company, but I'm not seeing how someone can determine how to build a URL for a request, since I don't know what mandatory parameters there are like that. I'm guessing I don't know where to look in Netsuite to find that configuration. Are there settings for base URLs somewhere?
Anyway, this is the script below that is being called. The other script is quite a bit longer, and has some company related info, so I'm not going to include it, but I don't think that matters anyway. The only thing that needs to be known is that we're using the URL above with nlapiRequestURL to call the script below, and calling 'getBody' on the result.
function suitelet(request, response){
nlapiLogExecution('DEBUG', 'suitelet', 'In the Preview_Link.suitelet function');
nlapiLogExecution('DEBUG', 'suitelet', 'The request is ' + JSON.stringify(request));
nlapiLogExecution('DEBUG', 'suitelet', 'The response is ' + JSON.stringify(response));
var context = nlapiGetContext();
var folderInternalId = context.getSetting('SCRIPT', 'custscript_buffer_folder_for_temp_pdf');
var strInternalIds = request.getParameter('internalIds');
if(!strInternalIds)
return;
var internalIds = strInternalIds.split(',');
if(!internalIds || internalIds.length == 0)
return;
var xml = "<?xml version=\"1.0\"?>\n<!DOCTYPE pdf PUBLIC \"-//big.faceless.org//report\" \"report-1.1.dtd\">\n";
xml += "<pdfset>";
for (var index = 0; index < internalIds.length; index++)
{
var internalId = internalIds[index];
xml += "<pdf src='"+ nlapiEscapeXML(nlapiLoadFile(internalId).getURL()) +"'/>";
}
xml += "</pdfset>";
var file = nlapiXMLToPDF(xml);
file.setName(internalIds[0] + '_' + internalIds.length + '_' + Math.random() +'.pdf');
file.setFolder(folderInternalId);
file.setIsOnline(true);
var internalIDUpdatedData = nlapiSubmitFile(file);
response.writeLine(internalIDUpdatedData);
}
So 'custscript_buffer_folder_for_temp_pdf' is the parameter defined on the Script record in Netsuite, and there is a value for it on that script's Deployment tab, on the Parameter tab, of '36', which is the value we want. That's our 'TEMP' folder in the File Cabinet. 'internalIds' is defined in the script that calls the one above, and it's either a single integer, or a comma separated list of them. (although it seems to always be a single value)
I should mention that this works in our production instance, and we just refreshed our Sandbox instance two days ago based on production. It didn't work in Sandbox prior to the refresh either. The only difference I can see, is that in production, the domain name is different in the URL, which of course it must be. All the code and settings appear to be identical besides that between the two environments.
Like I said, I'm new to Netsuite, so I haven't used nlapiRequestURL before. So this may be something incredibly stupid I'm overlooking. I've stepped through the code in the script that does the calling, but I can't seem to debug the Suitelet that is being called with nlapiRequestURL. I placed a nlapiLogExecution call as the first line in the script that is being called though, and it never even seems to hit that. I just get one of the errors I mentioned above before it actually steps into that script.
If anyone has any advice, I'd appreciate it. I've more or less exhausted my knowledge and resources. Thanks in advance, and let me know if you need further info.

Some things you may want to try:
var baseSuiteletURL = nlapiResolveURL('SUITELET', SUITELET_SCRIPT_ID, SUITELET_DEPLOYMENT_ID, true); //Getting URL from script id and deployment id
var reqUrl = baseSuiteletURL + "&internalids=17509"; //Adding your GET parameter here (all in lowercase)
var requestBody = {}; //Alternatively you could send your parameters within the POST body
requestBody.internalids = 17509;
var reqBody = JSON.stringify(requestBody);
var reqHeaders = {};
reqHeaders["User-Agent"] = "Mozilla/5.0"; //Sending user agent header to allow Suitelet to Suitelet calls
var myResponse = nlapiRequestURL(reqUrl, reqBody, reqHeaders);
Test sending your GET Query parameters (internalids) all in lowercase, I have experienced issues before in which the parameters case gets converted and it creates unnecessary troubles.

Related

setRedirectURLToSearchResults() not redirecting

I an new to NetSuite, and I am having a problem. I have created a button on a form through a user event script.
The button calls a client script, which executes a saved search. The search result should be displayed to the user.
Everything is located in one file:
function userEventBeforeLoad_AddSearchButton(type, form, request){
if(type == 'view' || type == 'edit'){
form.setScript('customscript_tvg_project_search_button');
var projectid = nlapiGetFieldValue('companyname');
form.addButton("custpage_search", "KHM Search", "performSearch('" + projectid + "')");
}
}
function performSearch(projectid) {
console.log('test in client');
alert('projectid = ' + projectid);
var obj = nlapiLoadSearch(null, 'customsearch_project_cost_detail_sublist');
obj.setRedirectURLToSearchResults();
}
I created a user event script record for userEventBeforeLoad_AddSearchButton(). and a client script record for performSearch().
In the code above, the button is created and the alert is being displayed. But no redirect is happening.
When I look at the result in Firebug it looks like this:
{"result":"\/app\/common\/search\/searchresults.nl?api=T","id":5}
Any ideas why the redirect is not happening, and what to do?
Edit: My code above was stripped down to simplify. The reason I am passing projectid over is that I actually need to filter the search result, suing the following two lines:
var searchFilter = new nlobjSearchFilter('job', null, 'anyof', projectid);
obj.addFilter(searchFilter)
Although the documentation does state that, "This method is supported in afterSubmit user event scripts, Suitelets, and client scripts", it seems from this NS User Group post by a Netsuite Employee in reply to someone who experienced the same issue as you, that the API does not actually perform the redirection client side:
Redirection works on server side. Use window.location.assign(url) to
navigate via script in client-side.
Testing this, I can see that setRedirectURLToSearchResults does appear to correctly "load the search into the session", so adding this line afterwards should fix your problem.
window.location = '/app/common/search/searchresults.nl?api=T';
setRedirectURLToSearchResults is not working for me either, but since you are using clientscript you might want to try this:
function performSearch(projectid) {
console.log('test in client');
alert('projectid = ' + projectid);
var searchid = 'customsearch_project_cost_detail_sublist';
location = '/app/common/search/searchresults.nl?searchid=' + searchid;
}

Twitter API attitude operator doesn't work

I have been using Twitters search API (via the node-twitter module) and I am running into an issue with the attitude operator. I am looking to get both positive and negative tweets about a certain subject. Currently though they return identical results. Here is my code that calls the API.
// Add since ID for future requests
twitterSearchClient.search(
{'q' :'xmas+%3A%28',
'lang' : 'en',
'count' : 100,
'result_type': 'recent'},
function(error, result) {
if (error) {
console.log('Error: ' + (error.code ? error.code + ' ' + error.message : error.message));
}
if (result) {
var requestDetails = result.search_metadata;
var results = result.statuses;
var resultsLength = results.length;
var r;
var data;
var d;
console.log(resultsLength);
for (var i=0; i<resultsLength; i++) {
console.log(r.text);
}
}
}
);
As you can see I am using the fully urlencoded value like the example in the documentation. I have also tried using xmas+%3A) for positive and xmas+%3A( for negative but this returns a 401 unauthorised error for some reason.
Has anyone ran into this issue or has a solution.
If you have any future questions I will be happy to answer them
Thanks in advance.
Per twitter's docs
Please, make sure to URL encode these queries before making the request
When going to that url and typing in your search xmas :) I got this query
https://twitter.com/search?q=xmas%20%3A)
as you can see xmas :) = xmas%20%3A) and xmas :( ends up being
https://twitter.com/search?q=xmas%20%3A(
xmas :( = xmas%20%3A(
If you still do not get different results (you can compare the results to what get when doing the search on the link above) then it is not the query, but maybe how you are making the call. Will need additional information if that is the case.
In this case, have you tried not encoding your query? The client may well be encoding it on your behalf.
...
'q': 'xmas :(',
...
NOTE: You do not mention, or link to which twitter client (via npm?) that you are using.
I can't speak for your specific issues, when I search via the form on the site I do get different results, and the queries are URI encoded... When you are passing input to a remote query string, I would recommend using encodeURIComponent('xmas :)') for your input values, this will yield the most predictable results.

Modify Node.js req object parameters

So as usual, I tried to find this question on SO but still no luck.
I am aware that the answer is "Yes, you CAN modify the req object" but nothing is said about the req object parameters.
For example the following code will throw an error:
app.get('/search', function(req, res) {
req.param('q') = "something";
});
Error:
ReferenceError: Invalid left-hand side in assignment
I imagine this has something to do with the property not having a 'SET' method or something along those lines.
There are a few scenarios where this could come in handy.
A service that turns quick-links into full blown requests and proxies those out.
Simply modifying the parameters before sending it off to another function that you have no desire to modify.
Onto the question, Is there a way to modify the req object parameters?
Rather than:
req.param('q') = "something";
You'll need to use:
req.params.q = "something";
The first one is trying to set a value on the return value of the param function, not the parameter itself.
It's worth noting the req.param() method retrieves values from req.body, req.params and req.query all at once and in that order, but to set a value you need to specify which of those three it goes in:
req.body.q = "something";
// now: req.param('q') === "something"
req.query.r = "something else";
// now: req.param('r') === "something else"
That said unless you're permanently modifying something submitted from the client it might be better to put it somewhere else so it doesn't get mistaken for input from the client by any third party modules you're using.
Alternative approaches to set params in request (use any):
req.params.model = 'Model';
Or
req.params['model'] = 'Model';
Or
req.body.name = 'Name';
Or
req.body['name'] = 'Name';
Or
req.query.ids = ['id'];
Or
req.query['ids'] = ['id'];
Now get params as following:
var model = req.param('model');
var name = req.param('name');
var ids = req.param('ids');
One can also imagine that the left-hand side is not a variable or property, so it makes no sense to try to assign a value to it. If you want to change a query param you have to modify the property in the req.query object.
req.query.q = 'something';
The same goes for req.body and req.params.

Assigning an object/method to a variable OR a new object literal

Looking at Google's code for their bookmark bubble library, I came across this:
var google = google || {};
google.bookmarkbubble = google.bookmarkbubble || {};
Could someone explain what it is they're doing here and why they are doing it? Since JS is an interpreted language why would you ever need to assign the same google object into the google variable? Wouldn't the script this is contained in only get executed once each time a page is loaded?
They are setting up a namespace-like object. In case there is already a google object or google.bookmarkbubble object, they are making sure they don't replace it. For example, if you had another Google library added, it may have already set up a google object that looks like this:
{
somelibrary: {
...
}
}
So if they just had:
var google = { bookmarkbubble: { ... } }
That would break any code that referenced google.somelibrary. Likewise, if they didn't have var google = google || {}; and you didn't have google already defined, then google.bookmarkbubble would throw an error.
In short, this code ensures that a google.bookmarkbubble object exists without overwriting any previously-defined google or google.bookmarkbubble objects.
var google = google || {}; is a shorthand syntax for
if (!google) {
google = {};
}
This ensures that the second assignment doesn't fail because google is at a least an empty hash.
This is most often done when one library contains a number of files which may or may not be included by the user. The first library will initialize google to an empty object (because it evaluates to nothing) and subsequent libraries will preserve properties already added to the object.

CouchDB list "An error occured accessing the list: Invalid JSON" error

I'm trying to call a CouchDB list from JavaScript.
My jQuery call is:
var listItems = $.couch.db('foo').list('foo/barList', 'foo/barView');
If I test this in the FireBug console when I'm on a page that has jquery.couch.js loaded (like Futon) it returns exactly what I want it to (several <input> tags with the appropriate data populated).
However, when I call this from code, I get the error:
An error occured accessing the list: Invalid JSON: [the html]
... where [the html] is the html I want to manipulate in my script. I don't understand why I'm getting a JSON error - I thought the point of lists was to return HTML. Is there a way to force it to return my html to me?
Also, my list function includes the following, so I'm not sure why this doesn't work.
start({
"headers": {
"Content-Type": "text/html"
}
});
According to https://issues.apache.org/jira/browse/COUCHDB-1059 this was a recognized bug and it had been patched. However after making the changes in jquery.couch.js recommended by Jan Lehnardt on the above page, I had to do one thing further.
The page above recommends making the following change in jquery.couch.js :
- var resp = httpData(req, "json");
+ var resp = httpData(req, dataType);
For some reason this didn't work for me but it did when I instead replaced it with the following. Theoretically one could add handlers for different types of content-types below.
var cType = req.getResponseHeader('Content-Type');
switch(cType) {
case 'text/html':
var resp = req.responseText;
break;
default:
var resp = $.parseJSON(req.responseText);
break;
}
If I'm missing something, I welcome recommendations on how to do this more effectively, but this works for me.

Resources