Netsuite POST data from a Portlet to a RESTlet in JSON - javascript

In NetSuite, I'm trying to create a form Portlet POST data to a RESTlet in JSON. I've checked the documentation and internet and all the examples I've been able to find are GET requests or they post to a backend Suitelet instead.
I've come to a point where I can make the request reach the RESTlet but it's not being formatted in JSON, so I get the following error:
Account: xxxxxxxxxxxxx
Environment: Production Date & Time: 6/11/2015 5:09 pm
Execution Time: 0.06s
Script Usage: 0
Script: gw_SS_FormBackend
Type: RESTlet
Function: postWMForm
Error: UNEXPECTED_ERROR
SyntaxError: Empty JSON string (null$lib#3)
I'm using the following code to set the submit button and it's working fine:
var headers = new Array();
headers['User-Agent-x'] = 'SuiteScript-Call';
headers['Authorization'] =
'NLAuth nlauth_account=' + cred.account +
', nlauth_email=' + cred.email +
', nlauth_signature=' + cred.password +
', nlauth_role=' + cred.role;
headers['Content-Type'] = 'application/json';
portlet.setSubmitButton(nlapiRequestURL(getRESTletURL(), null, headers, 'POST'), 'Submit', '_hidden');
My problem is I don't know how to convert the form data to JSON before submitting it.
I'd appreciate any help.

Why would you want to use a RESTlet? If you are in a portlet then you already have a valid NS session so you'd be better off using a Suitelet. A Suitelet you know is set up to handle JSON would be called thus:
nlapiRequestURL(suiteletURL', JSON.stringify{test:'it', when:new Date(), by:'Brett'}), {"content-type":'application/json'}, function(resp){console.log(resp.getBody());}, 'POST');
and your Suitelet code might include something like:
var body = request.getBody();
nlapiLogExecution('DEBUG', 'posted body', body);
var asJSON = JSON.parse(body);
var user = nlapiGetContext().getUser(); // system already knows who this is.
...
var obj = {
success:true,
stuff: asProcessed
};
response.setContentType('JAVASCRIPT');
response.writeLine( JSON.stringify(obj) );
Not quite as clean as a RESTlet but you avoid having to hack the credentials.

you can use JSON.stringify() function.
var headers = new Array();
headers['User-Agent-x'] = 'SuiteScript-Call';
headers['Authorization'] =
'NLAuth nlauth_account=' + cred.account +
', nlauth_email=' + cred.email +
', nlauth_signature=' + cred.password +
', nlauth_role=' + cred.role;
headers['Content-Type'] = 'application/json';
var myJsonHeader = JSON.stringify(headers);
portlet.setSubmitButton(nlapiRequestURL(getRESTletURL(), null, myJsonHeader, 'POST'), 'Submit', '_hidden');
Regards

Related

Cordova app has internet connection via emulator, but not on device [update: ajax problem]

I am trying to build a small app via cordova that sends data to a PERL-Script on a server. The debug and release build work fine on the genymotion emulator, however sending the data from Android phones does not work. There is no error message from the app (either, which is supposed to show up when the connection fails).
Running USB debugging, I do get the follwoing invoke error message (the savedataLastpage funtion is supposed to send the data):
Uncaught TypeError: Illegal invocation
at e (jquery-2.1.3.min.js:4)
at Ac (jquery-2.1.3.min.js:4)
at Function.n.param (jquery-2.1.3.min.js:4)
at Function.ajax (jquery-2.1.3.min.js:4)
at Object.saveDataLastPage (index.js:631)
at Object.renderLastPage (index.js:461)
at Object.recordResponse (index.js:597)
at HTMLButtonElement.<anonymous> (index.js:357)
at HTMLButtonElement.dispatch (jquery-2.1.3.min.js:3)
at HTMLButtonElement.r.handle (jquery-2.1.3.min.js:3)
The pertaining code is the following:
index.js:631
saveDataLastPage:function() {
$.ajax({
type: 'post',
url: '/URL/',
data: localStore,
crossDomain: true,
success: function (result) {
var pid = localStore.participant_id, snoozed = localStore.snoozed, uniqueKey = localStore.uniqueKey, pause_time=localStore.pause_time;
localStore.clear();
localStore.participant_id = pid;
localStore.snoozed = snoozed;
localStore.uniqueKey = uniqueKey;
localStore.pause_time=pause_time;
$("#question").html("<h3>Thank you, your responses have been sent.</h3>");
},
error: function (request, error) {
console.log(error);
$("#question").html("<h3>Error: Please check your internet connection.</h3><br><button>Send again</button>");
$("#question button").click(function () {app.saveDataLastPage();});
}
});
},
index.js:461
else {
var datestamp = new Date();
var year = datestamp.getFullYear(), month = datestamp.getMonth(), day=datestamp.getDate(), hours=datestamp.getHours(), minutes=datestamp.getMinutes(), seconds=datestamp.getSeconds(), milliseconds=datestamp.getMilliseconds();
localStore[uniqueKey + '.' + "completed" + "_" + "completedSurvey" + "_" + year + "_" + month + "_" + day + "_" + hours + "_" + minutes + "_" + seconds + "_" + milliseconds] = 1;
app.saveDataLastPage();
}
As stated before, on the genymotion emulator the ajax script works fine without the error and sends the data to the script.
I'm not sure why the emulator would work just fine but the error Uncaught TypeError: Illegal invocation suggests that it is a problem with the ajax post call. Specifically, the default setting of processing the data into a query string likely fails.
From the ajax documentation:
By default, data passed in to the data option as an object (technically, anything other than a string) will be processed and transformed into a query string, fitting to the default content-type "application/x-www-form-urlencoded". If you want to send a DOMDocument, or other non-processed data, set this option to false.
That means you can either turn off the processing processData: false which will post the data in the body of the request or you have to transform the localStore data into a proper object (from whatever it was before).
If it is like an object you can do the following:
var data = {};
for (var elem in localStore) {
data[elem] = localStore[elem];
}
or possibly in brief:
var data = {};
localStore.each(function(elem) { data[elem.name] = elem.value; });

How to make a Gmail API batch request from google apps script?

As an example, I need the batch request in the following scenario:
After using Gmail.Users.Threads.list(..) I would like to do several Gmail.Users.Threads.get(threadId,..) operations in a batch.
I am talking about something similar to gapi.client.newBatch(); call in javascript gmail api.
First in apps script, one needs to enable the Gmail v1 Api in Advanced Google Services as described here.
Then using the Gmail Api in google apps script looks like this:
The suggestions are:
Users : UsersCollection
newAutoForwarding() : AutoForwarding
newBatchDeleteMessagesRequest() : BatchDeleteMessagesRequest
newBatchModifyMessagesRequest() : BatchModifyMessagesRequest
newDraft() : Draft
newFilter() : Filter
newFilterAction() : FilterAction
newFilterCriteria() : FilterCriteria
newForwardingAddress() : ForwardingAddress
newImapSettings() : ImapSettings
newLabel() : Label
newLabelColor() : LabelColor
newMessage() : Message
newMessagePart() : MessagePart
newMessagePartBody() : MessagePartBody
newMessagePartHeader() : MessagePartHeader
newModifyMessageRequest() : ModifyMessageRequest
newModifyThreadRequest() : ModifyThreadRequest
newPopSettings() : PopSettings
newSendAs() : SendAs
newSmimeInfo() : SmimeInfo
newSmtpMsa() : SmtpMsa
newVacationSettings() : VacationSettings
newWatchRequest() : WatchRequest
There is no newBatch() suggested.
How about this answer? I couldn't find the method of batch request for Gmail.Users.Threads.get(). And at Google Apps Script, there are no methods for requesting the batch request. So it is required to implement the method. The flow of batch request is as follows.
Create the request body for the batch request.
Requst the body to the endpoint of POST https://www.googleapis.com/batch using multipart/mixed.
The access token is required to be used for only this post.
The sample script for this flow is as follows.
Sample script :
Flow :
Retrieve thread list using Gmail.Users.Threads.list().
Create the request body for Gmail.Users.Threads.get().
In this case, Gmail.Users.Threads.get() of Advanced Google Services cannot be used, so it is required to directly use the API.
Post the created body using multipart/mixed.
Parse the response.
Script :
function myFunction() {
var userId = "me"; // Please modify this, if you want to use other userId.
var threadList = Gmail.Users.Threads.list(userId).threads;
var body = threadList.map(function(e){
return {
method: "GET",
endpoint: "https://www.googleapis.com/gmail/v1/users/" + userId + "/threads/" + e.id
}
});
var url = "https://www.googleapis.com/batch";
var boundary = "xxxxxxxxxx";
var contentId = 0;
var data = "--" + boundary + "\r\n";
for (var i in body) {
data += "Content-Type: application/http\r\n";
data += "Content-ID: " + ++contentId + "\r\n\r\n";
data += body[i].method + " " + body[i].endpoint + "\r\n\r\n";
data += "--" + boundary + "\r\n";
}
var payload = Utilities.newBlob(data).getBytes();
var options = {
method: "post",
contentType: "multipart/mixed; boundary=" + boundary,
payload: payload,
headers: {'Authorization': 'Bearer ' + ScriptApp.getOAuthToken()},
muteHttpExceptions: true,
};
var res = UrlFetchApp.fetch(url, options).getContentText();
var dat = res.split("--batch");
var result = dat.slice(1, dat.length - 1).map(function(e){return e.match(/{[\S\s]+}/g)[0]});
Logger.log(result.length)
Logger.log(result)
}
Note :
The parsed response is an array. Each element in the array is corresponding to each element in the request body.
In this sample script, the thread list is retrieved by Gmail.Users.Threads.list("me").threads. If you want to use some threads, please modify the request body.
Reference :
Batching Requests
If I misunderstand your question, I'm sorry.

Fitbit OAuth API request, invalid signature

I am trying to make an API request to fitbit, using the oauth debugger from fitbit (https://dev.fitbit.com/apps/oauthtutorialpage) i am trying to figure out what i am doing wrong. I have added comments to my code below to help you understand what i am trying to achieve. What i am quite sure of is that i am either signing my request wrong, or using the wrong data to sign it. This is echoed by the API response.
I know there are more fitbit api questions here on stackoverflow, however did not find my answer there.
Is there anyone with more experience in Oauth signatures that knows what i could be doing wrong? Or could help me find a different approach to this?
var request = require('request');
var crypto = require('crypto');
var params = {
'oauth_consumer_key' : 'key12345',
'oauth_nonce' : Math.random().toString(36).substring(3), //random string
'oauth_signature_method' : 'HMAC-SHA1',
'oauth_timestamp' : Date.now().toString().substring(0,10), //timestamp with the same length as in the tutorial
'oauth_version' : '1.0'
}
var oauth_consumer_secret = 'secret123';
var post_string = 'POST&https://api.fitbit.com/oauth/request_token';
for(var key in params){
post_string += '&' + key + '=' + params[key];
}
/*At this point we have made a post string that we have to hash with hmac-sha1
the post string looks like this:
POST&https://api.fitbit.com/oauth/request_token&oauth_consumer_key=key12345&oauth_nonce=az6r8cqlzyqfr&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1439147378&oauth_version=1.0
The post_string from the tutorial looks like this:
POST&%2Foauth%2Frequest_token&oauth_consumer_key%3D%26oauth_nonce%3D%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1439145944%26oauth_version%3D1.0
*/
var hmac = crypto.createHmac('sha1', oauth_consumer_secret + "&");
// The tutorial page shows me the signature was 'signed with secret&'. I have tried with and without the & at the end, but without luck.
hmac.setEncoding('base64'); //i'm not sure if this is correct
hmac.write(post_string);
hmac.end();
var hash = hmac.read();
//and finally adding the hash to the parameters.
params.oauth_signature = hash;
//now, making the request with an authorization header.
var header='';
for (var key in params){
if(header.length === 0){
header = ' OAuth ' + key + '="' + params[key] + '"';
}
else{
header += ', ' + key + '="' + params[key] + '"';
}
}
/*
At this point the header parameter looks like this
OAuth oauth_consumer_key="key12345", oauth_nonce="jnr97ppvjs2lnmi", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1439148049", oauth_version="1.0", oauth_signature="random_signature"
The tutorial tells me to use the headers:
OAuth oauth_consumer_key="key12345", oauth_nonce="jnr97ppvjs2lnmi", oauth_signature="different_signature", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1439145944", oauth_version="1.0"
*/
var headers ={
'Authorization' : header
}
var url="https://api.fitbit.com/oauth/request_token";
var requestTimeout = 5000;
var opts = {
url: url,
timeout: requestTimeout,
headers : headers
}
request(opts, function (err, res, body) {
if (err) {
console.dir(err);
return;
}
var statusCode = res.statusCode;
if(res.statusCode === 200){
console.log(body);
}
else{
console.log("http-error-code: " + res.statusCode);
console.log(body);
}
})
/*
The response:
http-error-code: 401
{"errors":[{"errorType":"oauth","fieldName":"oauth_signature","message":"Invalid signature: 9fXI85C7GvZqMyW1AK1EkOSWZCY="}],"success":false}
*/
To get access token and secret use Grant (you can test FitBit in the playground).
Once you have access token and secret use Purest to make subsequent request to the FitBit API.
Here is an example on how to get the user's profile:
var Purest = require('purest')
var fitbit = new Purest({provider:'fitbit',
key:'[CONSUMER_KEY]', secret:'[CONSUMER_SECRET]'})
fitbit.get('user/-/profile', {
oauth:{token:'[ACCESS_TOKEN]', secret:'[ACCESS_SECRET]'}
}, function (err, res, body) {})
Alternatively you can use request for that:
var request = require('request')
request.get('https://api.fitbit.com/1/user/-/profile.json', {
oauth:{
consumer_key:'..',
consumer_secret:'..',
token:'..',
token_secret:'..'
}
}, function (err, res, body) {})
In short - don't try to implement the web server OAuth flow by yourself - use Grant, then use either Purest or request, just keep in mind that you don't have to pass all of the OAuth parameters by yourself, just pass the credentials.

Creating a ticket in Freshdesk from Google Apps Script

I am trying to write some code that submits a ticket automatically with information from a page I created in Apps Script. I have tried numerous examples, but I can't seem to get my code to work.
var headers = {
'Content-type': 'application/json',
'Authorization': 'Basic ' + Utilities.base64Encode(API_KEY + ':X')
};
//Puts together the ticket according to the freshdesk api.
//var payload = '{"helpdesk_ticket":{"description":"' + message + '","subject":"' + subject + '","email":"' + arr[0][0] + '","priority":"' + ticketPriority + '","status":2}}';
//var payload = '{"helpdesk_ticket":{"description": message ,"subject": subject,"email": arr[0][0],"priority": ticketPriority,"status":2}}';
var payload = '{"helpdesk_ticket":{"description":"TEST","subject":"TEST","email":"test#test.com","priority":1,"status":2}}';
//Adds the extensions that are needed to post a new ticket to the end of the url
var url = ENDPOINT + '/helpdesk/tickets.json';
var options = {
'method': 'post',
'headers': headers,
'payload': payload,
muteHttpExceptions: true
};
var response = UrlFetchApp.fetch(url, options);
This is what I currently have. I have gotten it to work once, but only when I do not have any variables being assigned to the 'description' or 'subject' header (the line with the payload variables that is uncommented. When I use that line, a ticket is successfully created). I am not sure why my first or second lines with the payload variables would not work. The variable 'message' is just a String with some new line characters '\n' in it. Does anyone know why this might be happening?
Solved by building the message variable with HTML code and using the 'description_html' property instead of 'description'.

Node.js HTTP Strings

Given a full HTTP message string, is there facilities in Node.js to parse this? Basically I'm not going to be using the http.createServer function. I already have the full HTTP message through other means (ZMQ), but I need to parse it, which means headers, query strings, post body.. etc.
Furthermore, the same functionality should be able to reverse the process, that is creating a fully formed HTTP response message string. That I can use and pass through different transport (ZMQ).
I know that PHP has Symfony's HTTP Foundation (which does what I'm asking). Is there something in Node.js that does a similar thing?
I found this: https://github.com/joyent/http-parser but it's in C++
You can use the http-parser from within node, but it's not very well documented. I extracted the following example from the tests:
var HTTPParser = process.binding('http_parser').HTTPParser,
CRLF = '\r\n',
request = new Buffer('POST /it HTTP/1.1' + CRLF +
'Content-Type: application/x-www-form-urlencoded' + CRLF +
'Content-Length: 15' + CRLF +
CRLF +
'foo=42&bar=1337'),
parser = new HTTPParser(HTTPParser.REQUEST),
headers = {},
body = '';
parser.onHeadersComplete = function(info) {
headers = info;
};
parser.onBody = function(b, start, len) {
body = b.slice(start, start + len).toString();
};
parser.onMessageComplete = function() {
console.log('message complete');
console.log('request method: ' + headers.method);
console.log('request body:\n\n ' + body);
};
parser.execute(request, 0, request.length);
Gives the following output:
message complete
request method: POST
request body:
foo=42&bar=1337

Categories