Search not working in restlet SuiteScript 2.0 - netsuite

I am trying to use search.create in RESTlet to check whether a contact exist in my netsuite account or not. This is my code
define(['N/record','N/search'],
function(record,search) {
function dosearch(contact_mail){
var result;
try{
result = search.create({
type : record.Type.CONTACT,
filters:['email','IS',contact_mail],
columns: ['email']
}).run().getRange({
start: 0,
end: 1
});
log.debug(result);
} catch(e){
result = e;
}
return result;
}
function doGet(requestParams) {
var contact_mail = requestParams.email;
log.debug(contact_mail);
var result = dosearch(contact_mail);
return result;
}
And I am accesing this RESTlet from my system using this node js code.
async function myGet(){
var para = {id: 'my_id' , email: 'email'};
var accountSettings = {
//'Content-Type': 'application/json',
//params: para,
accountId: account_id,
tokenKey: token_key,
tokenSecret: token_secret,
consumerKey: consumer_key,
consumerSecret: consumer_secret };
var urlSettings = {
url: link
}
var myInvoices = nsrestlet.createLink(accountSettings, urlSettings)
try{
var res = await myInvoices.get(para);
console.log(res);
} catch(e){
console.log(e);
}
}
Is there something wrong with the code? I tried same search code with user event script and it worked there but its not working in RESTlet. Can someone help me with this.

Silently failing a search is an indicator that the role you are using with your RESTlet doesn't have access to contacts. I generally see this with transactions I'd expect contacts work similarly because under the covers a contact is a form of entity.

Related

Bot Framework v4 Node.js Location

I'm in the process of designing a chat bot and trying to find some Node.js sample code and/or documentation on how to implement the Azure Maps service as part of Bot Framework V4. There are many examples of how this is accomplished in V3, but there seems to be no examples of a V4 solution for Node.js. I'm looking to create a step in my botbuilder-dialog flow that would launch a simple "where do we ship it too" location dialog that would guide the user through the dialog and store the address results as part of that users profile. Any help or advice on this would be appreciated.
Yes, this is doable. I created a class (probably overkill, but oh well) in which I make my API call, with my supplied parameters, to get the map. I decided to use Azure Maps (vs Bing Maps) only because I was curious in how it differed. There isn't any reason you couldn't do this with Bing Maps, as well.
In the bot, I am using a component dialog because of how I have the rest of my bot designed. When the dialog ends, it will fall off the stack and return to the parent dialog.
In my scenario, the bot presents the user with a couple choices. "Send me a map" generates a map and sends it in an activity to the client/user. Anything else sends the user onward ending the dialog.
You will need to decide how you are getting the user's location. I developed this with Web Chat in mind, so I am getting the geolocation from the browser and returning it to the bot to be used when getMap() is called.
const { ActivityTypes, InputHints } = require('botbuilder');
const fetch = require('node-fetch');
class MapHelper {
async getMap(context, latitude, longitude) {
var requestOptions = {
method: 'GET',
headers: {
'Content-Type': 'application/json'
},
redirect: 'follow'
};
const result = await fetch(`https://atlas.microsoft.com/map/static/png?subscription-key=${ process.env.AZURE_MAPS_KEY }&api-version=1.0&layer=basic&zoom=13&center=${ longitude },${ latitude }&language=en-US&pins=default|al.67|la12 3|lc000000||'You!'${ longitude } ${ latitude }&format=png`, requestOptions)
.then(response => response.arrayBuffer())
.then(async result => {
const bufferedData = Buffer.from(result, 'binary');
const base64 = bufferedData.toString('base64');
const reply = { type: ActivityTypes.Message };
const attachment = {
contentType: 'image/png',
contentUrl: `data:image/png;base64,${ base64 }`
};
reply.attachments = [attachment];
await context.sendActivity(reply, null, InputHints.IgnoringInput);
})
.catch(error => {
if (error) throw new Error(error);
});
return result;
};
};
module.exports.MapHelper = MapHelper;
const { ChoicePrompt, ChoiceFactory, ComponentDialog, ListStyle, WaterfallDialog } = require('botbuilder-dialogs');
const { MapHelper } = require('./mapHelper');
const CONFIRM_LOCALE_DIALOG = 'confirmLocaleDialog';
const CHOICE_PROMPT = 'confirmPrompt';
class ConfirmLocaleDialog extends ComponentDialog {
constructor() {
super(CONFIRM_LOCALE_DIALOG);
this.addDialog(new ChoicePrompt(CHOICE_PROMPT))
.addDialog(new WaterfallDialog(CONFIRM_LOCALE_DIALOG, [
this.askLocationStep.bind(this),
this.getMapStep.bind(this)
]));
this.initialDialogId = CONFIRM_LOCALE_DIALOG;
}
async askLocationStep(stepContext) {
const choices = ['Send me a map', "I'll have none of this nonsense!"];
return await stepContext.prompt(CHOICE_PROMPT, {
prompt: 'Good sir, may I pinpoint you on a map?',
choices: ChoiceFactory.toChoices(choices),
style: ListStyle.suggestedAction
});
}
async getMapStep(stepContext) {
const { context, context: { activity } } = stepContext;
const text = activity.text.toLowerCase();
if (text === 'send me a map') {
const { latitude, longitude } = activity.channelData;
const mapHelper = new MapHelper();
await mapHelper.getMap(context, latitude, longitude);
const message = 'Thanks for sharing!';
await stepContext.context.sendActivity(message);
return await stepContext.endDialog();
} else {
await stepContext.context.sendActivity('No map for you!');
return await stepContext.endDialog();
}
}
}
module.exports.ConfirmLocaleDialog = ConfirmLocaleDialog;
module.exports.CONFIRM_LOCALE_DIALOG = CONFIRM_LOCALE_DIALOG;
Hope of help!
---- EDIT ----
Per request, location data can be obtained from the browser using the below method. It is, of course, dependent on the user granting access to location data.
navigator.geolocation.getCurrentPosition( async (position) => {
const { latitude, longitude } = position.coords;
// Do something with the data;
console.log(latitude, longitude)
})

Having 2 auth clients results in 404 on calendar event creation

I'm trying to create 2 calendar events across 2 calendars using the Google Node.js API client library in a single function. I'm using 2 separate auth objects that I'm retrieving like so:
var auth1 = await google.auth.getClient({ credentials: config.account1Creds, scopes: ["https://www.googleapis.com/auth/calendar.events"] });
var auth2 = await google.auth.getClient({ credentials: config.account2Creds, scopes: ["https://www.googleapis.com/auth/calendar.events"] });
I can create the event on the first calendar fine, but when I create the event on the 2nd calendar, I get a 404 Not Found back from the Google Calendar API server.
If I comment out the first var auth1 = await google.auth... line and only create the event on the 2nd calendar, everything is fine and the event is created successfully.
It kinda feels as if the first time getClient is called, it sets a global auth object that gets used for all remaining API requests and it can't be replaced, but thats just a hunch I have and I don't actually know.
Anybody have any ideas why this is happening?
EDIT:
GetGoogleCalendarService: async function(credentials)
{
var auth = await google.auth.getClient({ credentials: credentials, scopes: ["https://www.googleapis.com/auth/calendar.events"] });
return google.calendar({ version: "v3", auth: auth });
},
InsertCalendarEvent: function(calendar, entry, callback)
{
calendar.events.insert(entry, callback);
},
SendInvitesToEvent: async function (request, response)
{
//build the calendar event
var entry = {
...
}
//insert into operations calendar
var opsCal = await Events.GetGoogleCalendarService(config.GetConfig().OpsCalendarCredentials);
Events.InsertCalendarEvent(mainCal, entry);
//insert into public calendar
var publicCal = await Events.GetGoogleCalendarService(config.GetConfig().PublicCalendarCredentials);
Events.InsertCalendarEvent(publicCal, entry, async function(err, event)
{
//err: 404 Not Found
...
}
}
You want to insert events to 2 calendars using 2 clients.
Client "A" inserts an event to Calendar "A".
Client "B" inserts an event to Calendar "B".
You are using googleapis of Node.js.
If my understanding is correct, how about this modification? In this modification, I separated retrieving auth by each credential.
Modified script:
const { google } = require("googleapis");
function insertEvent(calendar, calendarId) {
// insert event
}
async function getService(c) {
var auth = await google.auth.getClient({
credentials: c,
scopes: ["https://www.googleapis.com/auth/calendar.events"]
});
return google.calendar({ version: "v3", auth: auth });
}
function main() {
getService(config.account1Creds).then(calendar => {
insertEvent(calendar, "### calendarId ###");
});
getService(config.account2Creds).then(calendar => {
insertEvent(calendar, "### calendarId ###");
});
}
main();
Note:
This is a sample script. So please modify this for your situation.
In my environment, I could confirm that this script works. But if this didn't work and this modification was not the result you want, I apologize.
Edit:
From your current script, I modified as follows.
Is opsCal mainCal? In my modification, opsCal is used as mainCal.
Modified script:
From:
//insert into operations calendar
var opsCal = await Events.GetGoogleCalendarService(config.GetConfig().OpsCalendarCredentials);
Events.InsertCalendarEvent(mainCal, entry);
//insert into public calendar
var publicCal = await Events.GetGoogleCalendarService(config.GetConfig().PublicCalendarCredentials);
Events.InsertCalendarEvent(publicCal, entry, async function(err, event)
{
//err: 404 Not Found
...
}
To:
Events.GetGoogleCalendarService(config.GetConfig().OpsCalendarCredentials).then(
opsCal => {
Events.InsertCalendarEvent(opsCal, entry);
}
);
Events.GetGoogleCalendarService(config.GetConfig().PublicCalendarCredentials).then(
publicCal => {
Events.InsertCalendarEvent(publicCal, entry);
}
);

Creating form in netsuite using suitscript 2.0

var formData = new FormData();
formData.append("name", "John");
formData.append("age", "31");
for (var value of formData.values()) {
log.debug(value);
}
but when i want to log form values using formData api. It's giving below error.
ReferenceError: "FormData" is not defined.
FormData is a client side API managed under XMHttpRequest
UserEvent scripts are server side scripts with no browser based APIs available at all.
So you could use FormData in a client script to send info to a Suitelet or RESTlet but it's not present in a UserEvent script.
If you want to create a form in a Suitelet using SS2.0 you can use the following as a sample:
/**
*#NApiVersion 2.x
*#NScriptType Suitelet
*/
define(["N/log", "N/redirect", "N/runtime", "N/ui/serverWidget", "N/url", "./kotnRECBCFilters"],
function (log, redirect, runtime, ui, url, kotnRECBCFilters_1) {
function showPropertiesForm(context) {
var form = ui.createForm({
title: 'Property Trust Ledger'
});
var req = context.request;
var fromLoc = form.addField({
id: 'custpage_loc',
type: ui.FieldType.SELECT,
label: 'For Property',
source: 'location'
});
fromLoc.updateLayoutType({ layoutType: ui.FieldLayoutType.NORMAL });
fromLoc.updateBreakType({ breakType: ui.FieldBreakType.STARTCOL });
if (req.parameters.custpage_loc) {
fromLoc.defaultValue = req.parameters.custpage_loc;
}
var notAfterDate = form.addField({
id: 'custpage_not_after',
type: ui.FieldType.DATE,
label: 'On or Before'
});
if (req.parameters.custpage_not_after) {
notAfterDate.defaultValue = req.parameters.custpage_not_after;
}
form.addSubmitButton({
label: 'Get Detail'
});
//... bunch of stuff removed
context.response.writePage(form);
}
function onRequest(context) {
if (context.request.method === 'POST') {
var currentScript = runtime.getCurrentScript();
var params = {};
for (var k in context.request.parameters) {
if (k.indexOf('custpage_') == 0 && k.indexOf('custpage_transactions') == -1) {
if ((/^custpage_.*_display$/).test(k))
continue;
params[k] = context.request.parameters[k];
}
}
redirect.toSuitelet({
scriptId: currentScript.id,
deploymentId: currentScript.deploymentId,
parameters: params
});
return;
}
showPropertiesForm(context);
}
exports.onRequest = onRequest;
});

How to invoke/use the ranker ID from within the nodeJs code to call Retrieve & Rank API?

I have tried a successful approach in Watson Retrieve and Rank, to set the 'ranker_id' from within the code snippet versus setting it as an environment variable.
Below is the code snippet:
var qs = require('querystring');
// search documents
var ranker_id = 'replace with ID';
var question = payload.input.text; //Only the question is required
var query = **qs.stringify({q: question, ranker_id: ranker_id, fl: 'id,title,contentHtml'});**
solrClient.get('fcselect', query, function(err, searchResponse) {...}.
In some versions of npm, qs also works-
var qs = require('qs');
**This would be the requirement to deploy in all production architecture, where the code would reside in production servers & make calls to the API. In such a scenario, env variable(ranker_id) could not be set in production environment, hence this approach
You can see in this documentation here and see the all examples how to use Retrieve and Rank - IBM Watson.
In this case the password and username is Service Credentials.
One example search and rank:
var watson = require('watson-developer-cloud');
var retrieve_and_rank = watson.retrieve_and_rank({
username: '{username}',
password: '{password}',
version: 'v1'
});
var params = {
cluster_id: 'sc1ca23733_faa8_49ce_b3b6_dc3e193264c6',
collection_name: 'example_collection'
};
// Use a querystring parser to encode output.
var qs = require('qs');
// Get a Solr client for indexing and searching documents.
// See https://github.com/watson-developer-cloud/node-sdk/blob/master/services/retrieve_and_rank/v1.js
solrClient = retrieve_and_rank.createSolrClient(params);
var ranker_id = 'B2E325-rank-67'; //PASTE YOUR RANKER_ID
var question = 'what is the basic mechanism of the transonic aileron buzz';
var query = qs.stringify({q: question, ranker_id: ranker_id, fl: 'id,title'});
solrClient.get('fcselect', query, function(err, searchResponse) {
if(err) {
console.log('Error searching for documents: ' + err);
}
else {
console.log(JSON.stringify(searchResponse.response.docs, null, 2));
}
});
See one example how Get information about a ranker:
var watson = require('watson-developer-cloud');
var retrieve_and_rank = watson.retrieve_and_rank({
username: '{username}', //username from Service Credentials Retrieve and Rank
password: '{password}', // password from Service Credentials Retrieve and Rank
version: 'v1'
});
var params = {
ranker_id: 'B2E325-rank-67', //PASTE YOUR RANKER_ID
};
retrieve_and_rank.rankerStatus(params,
function(err, response) {
if (err)
console.log('error:', err);
else
console.log(JSON.stringify(response, null, 2));
});
Example Index documents:
//require watson
var watson = require('watson-developer-cloud');
//call with your password and username from Service Retrieve and Rank Credentials
var retrieve_and_rank = watson.retrieve_and_rank({
username: '{username}',
password: '{password}',
version: 'v1'
});
//cluster id from your documentation
var params = {
cluster_id: 'sc1ca23733_faa8_49ce_b3b6_dc3e193264c6',
collection_name: 'example_collection',
};
// your doc here
var doc = {
id: 1,
author: 'brenckman,m.',
bibliography: 'j. ae. scs. 25, 1958, 324.',
body: 'experimental investigation of the aerodynamics of a wing in a slipstream. an experimental study of a wing in a propeller slipstream was made in order to determine the spanwise distribution of the lift increase due to slipstream at different angles of attack of the wing and at different free stream to slipstream velocity ratios.',
title: 'experimental investigation of the aerodynamics of a wing in a slipstream'
};
//Get a Solr client for indexing and searching documents with rankerid inside params variable
solrClient = retrieve_and_rank.createSolrClient(params);
console.log('Indexing a document...');
solrClient.add(doc, function (err, response) {
if (err) {
console.log('Error indexing document: ', err);
}
else {
console.log('Indexed a document.');
solrClient.commit(function(err) {
if(err) {
console.log('Error committing change: ' + err);
}
else {
console.log('Successfully committed changes.');
}
});
}
});

UCWA Lync authentication - 500 web ticket is invalid

I'm trying to create a simple client for Lync using Nodejs.
Base on http://ucwa.lync.com/documentation/KeyTasks-CreateApplication I've made someting like this.
It works until last step #9 when I should register my app with UCWA. Server responds with code 500 and silly explanation
There is a problem with the resource you are looking for, and it cannot be displayed
And headers
x-ms-diagnostics': '28032;source="mysource";reason="The web ticket is invalid."'
var http = require('request-promise');
var lync = {};
lync.setup = function(email, password){
var self = this;
var hostname = email.split('#');
this.username = email;
//discover urls
return http.get('http://lyncdiscover.'+hostname[1])
.then(function(d) {
var parsed = JSON.parse(d);
self.urls = {
self: parsed._links.self.href,
user: parsed._links.user.href,
xframe: parsed._links.xframe.href
};
return http.get(self.urls.user);
}).catch(function(err){
if(err.statusCode == 401){
var toParse = err.response.headers['www-authenticate'];
var Oauth = toParse.match(/https:\/\/[\d\w\./_-]*/i)[0];
var loginPost = {
grant_type: 'password',
username: email,
password: password
};
return http.post(Oauth, {form:loginPost});
}
return false
}).then(function(data){
var parsed = JSON.parse(data);
//setup authorization
http = http.defaults({
headers: {Authorization: parsed.token_type + ' ' + parsed.access_token}
});
//console.log(self.urls.user);
//console.log('Authorization:'+ parsed.token_type + ' ' + parsed.access_token);
return http.get(self.urls.user);
}).then(function(data){
var parsed = JSON.parse(data);
self.urls.applications = parsed._links.applications.href;
var registerApp = {
culture : "en-us",
endpointId : "2d9dc28d-4673-4035-825c-feb64be28e4e",
userAgent : "Test"
};
var r = "{'userAgent': 'NodeJs', 'endpointId' : '2d9dc28d-4673-4035-825c-feb64be28e4e', 'culture': 'en-US'}";
return http.post(self.urls.applications, {body: registerApp, json:true});
})
.then(function(data){
console.log(data);
})
.catch(function(err){
console.log(err);
return false;
});
};
//run app
lync.setup('login#domain.com', 'password').then(function(ret){
});
One key point here. It's not my server. I just have an account over there and I can login with official Lync client or Pidgin plugin.
Are there some extra steps to "allow" my app to work with UCWA?
#ShelbyZ
I can easily authorize using Oauth. I'm receiving authorization token so I'm logged in.
I'm receiving json similar to
"_links":{
"self":{"href":"link"},
"applications":{"href":"i need this"},
"xframe":{"href":"link"}
} }
Now. I need to "register my application" doing POST.
In this last step I get 500 code response.
I hope It's not related with that #Matthew Proctor said..
becouse I cannot simple administrate the server
Thank you #ShelbyZ
You were right, it was split-domain scenario. Now authorization works, and I can register my app. Also example for future generations
var http = require('request-promise');
var lync = {};
lync._authorize = function(){
var self = this;
var orgDomain = self.urls.user.match(/https:\/\/([\w\d\.]+)/i)[0];
//console.log(orgDomain);
http.get(self.urls.user).catch(function(err){
if(err.statusCode == 401){
var toParse = err.response.headers['www-authenticate'];
var Oauth = toParse.match(/https:\/\/[\d\w\./_-]+/i)[0];
var loginPost = {
grant_type: 'password',
username: self.username,
password: self.password
};
return http.post(Oauth, {form:loginPost});
}
}).then(function(data){
if(data) {
var parsed = JSON.parse(data);
//setup authorization
http = http.defaults({
headers: {Authorization: parsed.token_type + ' ' + parsed.access_token}
});
return http.get(self.urls.user);
}
}).then(function(data){
//check for split-domain scenario
var parsed = JSON.parse(data);
var domain = parsed._links.self.href.match(/https:\/\/([\w\d\.]+)/i)[0];
console.log('[1] '+orgDomain);
console.log('[2] '+domain);
if(domain!== orgDomain){
//split domain scenario
self.urls.user = self.urls.user.replace(orgDomain, domain);
http = http.defaults({
headers: {Authorization: null}
});
self._authorize();
} else { //create app
var parsed = JSON.parse(data);
self.urls.applications = parsed._links.applications.href;
var registerApp = {
culture : "en-us",
endpointId : "2d9dc28d-4673-4035-825c-feb64be28e4e",
userAgent : "NodeJs client"
};
return http.post(self.urls.applications, {body: registerApp, json:true});
}
}).then(function(app){
console.log(app);
});
};
lync.setup = function(email, password){
var self = this;
var hostname = email.split('#');
this.username = email;
this.password = password;
//discover urls
return http.get('http://lyncdiscover.'+hostname[1])
.then(function(d) {
var parsed = JSON.parse(d);
self.urls = {
self: parsed._links.self.href,
user: parsed._links.user.href,
xframe: parsed._links.xframe.href
};
return self._authorize();
});
};
//run app
lync.setup('username#domain.com', 'password');
I was getting the same error before I added my test domain to the list of Allowed Domains.
This can be updated via PowerShell, full details below:
Enabling UCWA and Configuring Allowed Domains
I've had clients see similar errors when running my code from http://localhost/, their fix was to test their code on a FQDN (ie http://testmyucwacode.mydomain.com/).

Resources