How can I create prototype objects inside Zapier? - zapier

I am working on our Application-integration inside Zapier and using zapier's built in methods for polling data. The script is now long and use a lot of repetitive function objects. How can I use prototype inheritance model for each attribute call so that I can reuse it for similar calls for other attributes? A typical api call is as follows:
var Zap = {
myattribute_post_poll: function(bundle) {
var results = JSON.parse(bundle.response.content);
results.value.reverse();
//attribute call
var cRequest = {
'url': "myURL.com/a/" + bundle.auth_fields.tenant_id +
"/odata/standard.odata/Catalog_attibute(guid'" + results.value[i].attribute_Key + "')?$format=json",
'headers': {
"Authorization": "Basic " + btoa(bundle.auth_fields.username + ':' + bundle.auth_fields.password)
},
'method': "GET"
};
var cResponse = z.request(cRequest);
try{
var JSONResponse = JSON.parse(cResponse.content);
results.value[i].Customer_name = JSONResponse.Description;
} catch(error){
console.log(error);
results.value[i].Customer_name = results.value[i].Company_Key;
}
return results;
}

The best technique is just to break into reusable properties on Zap:
var Zap = {
reusable_thing: function(arg) {
return arg + 1;
},
attr_pre_poll: function(bundle) {
bundle.request.params = Zap.reusable_thing(1);
return bundle.request;
}
};

This might be helpful to others, so adding my prototype object here:
var Zap = {
attrGuidRequest : function(obj, document, key){
var attrResponse = JSON.parse(
z.request({
'url': "myUrl" + tenant_id +
"/odata/.." + document + "(guid'" + key + "')?$format=json",
'headers': {
"Authorization": "Basic " + btoa(username + ':' + password)
},
'method': "GET"
}).content) || {};
return attrResponse;
}
}

Related

Get term for selected autocomplete when multiple are on one page

I have a page where I am adding jquery-ui autocompletes dynamically
My .autocomplete() code includes a $.getJSON('my_url', my_payload) where, in my_payload,' I am trying to send the request.term (what I typed into the jqueryui textbox) as well as the id of the jquery ui text box.
The problem is, for all the dynamically added textboxes, they were just picking up the term and id of the original autocomplete.
I managed to find a way to get the id of the added (not original) autocomplete by wrapping the autocomplete in a function that has the added field passed in as a parameter, but because the 'term' is in the request, which comes from .autocomplete, I do not know how to get this for the new ones.
https://jsfiddle.net/amchugh89/1L8jvea5/4/
//=======dynamic formset script from https://medium.com/all-about-
django/adding-forms-dynamically-to-a-django-formset-375f1090c2b0======
function updateElementIndex(el, prefix, ndx) {
var id_regex = new RegExp('(' + prefix + '-\\d+)');
var replacement = prefix + '-' + ndx;
if ($(el).attr("for")) $(el).attr("for", $(el).attr("for").replace(id_regex, replacement));
if (el.id) el.id = el.id.replace(id_regex, replacement);
if (el.name) el.name = el.name.replace(id_regex, replacement);
}
function cloneMore(selector, prefix) {
var newElement = $(selector).clone(true);
var total = $('#id_' + prefix + '-TOTAL_FORMS').val();
newElement.find(':input:not([type=button]):not([type=submit]):not([type=reset])').each(function() {
if ($(this).attr('name')){
var name = $(this).attr('name').replace('-' + (total-1) + '-', '-' + total + '-');
var id = 'id_' + name;
$(this).attr({'name': name, 'id': id}).val('').removeAttr('checked');
if($(this).attr('id').includes('gl')){
console.log($(this).attr('id'))
make_autocomplete($(this))
}
}
});
newElement.find('label').each(function() {
var forValue = $(this).attr('for');
if (forValue) {
forValue = forValue.replace('-' + (total-1) + '-', '-' + total + '-');
$(this).attr({'for': forValue});
}
});
total++;
$('#id_' + prefix + '-TOTAL_FORMS').val(total);
$(selector).after(newElement);
var conditionRow = $('.form-row:not(:last)');
conditionRow.find('.btn.add-form-row')
.removeClass('btn-success').addClass('btn-danger')
.removeClass('add-form-row').addClass('remove-form-row')
.html('<span class="glyphicon glyphicon-minus" aria-hidden="true"></span>');
return false;
}
function deleteForm(prefix, btn) {
var total = parseInt($('#id_' + prefix + '-TOTAL_FORMS').val());
if (total > 1){
btn.closest('.form-row').remove();
var forms = $('.form-row');
$('#id_' + prefix + '-TOTAL_FORMS').val(forms.length);
for (var i=0, formCount=forms.length; i<formCount; i++) {
$(forms.get(i)).find(':input').each(function() {
updateElementIndex(this, prefix, i);
});
}
}
return false;
}
$(document).on('click', '.add-form-row', function(e){
e.preventDefault();
cloneMore('.form-row:last', 'form');
return false;
});
$(document).on('click', '.remove-form-row', function(e){
e.preventDefault();
deleteForm('form', $(this));
return false;
});
//====================
//AUTOCOMPLETE==(that allows for multiple ACs
https://stackoverflow.com/questions/24656589/using-jquery-ui-autocomplete-
with-multiple-input-fields)===================================
function make_autocomplete(ee) {
ee.on("focus", function(){ //.autocomplete({
$(this).autocomplete({
minLength: 2,
source: function( request, response ) {
var term = request.term;
//with the formset, I want to get the row for which I am typing in the
'term'
var this_formset_row_autocomplete_id
=ee.attr('id');//$(this.element).prop("id");//
$(this).attr('id');
console.log(this_formset_row_autocomplete_id);
var corresponding_branch_html_id =
this_formset_row_autocomplete_id.replace('gl_account','branch');
var this_formset_row_branch_sym_id =
$('#'+corresponding_branch_html_id).val();
//console.log(corresponding_branch_html_id, this_formset_row_branch_sym_id)
var appended_data={term:term,
this_formset_row_branch_sym_id:this_formset_row_branch_sym_id};
console.log(appended_data);
$.getJSON( "{% url 'dashapp:account_autocomplete' %}", appended_data,
function( data,
status, xhr ) {
//cache[ term ] = data;
response( data );
});
}
});
});
}//end function make_autocomplete
var ee =$( ".account_autocomplete" )
make_autocomplete(ee)
//===============
You may want to try to make it more simple for testing. Something like:
function make_autocomplete(obj) {
obj.autocomplete({
minLength: 2,
source: function(req, resp) {
var myData = {
term: req.term,
original_form_branch_id: $(this).closest("form").attr("id"),
this_formset_row_branch_sym_id: $(this).closest(".row").find("select").val()
}
$.getJSON("myurl", myData, function(results) {
resp(results);
});
}
});
}
Fiddle: https://jsfiddle.net/Twisty/pywb9nhv/23/
This uses .closest() to gather details from the relative objects. Also I do not see any benefit to initializing Autocomplete on focus event.
If you would like further help, please provide Example Data that can be used in a working example.
Hope that helps a little.

Not able to use the updated value -- Flutter

I'm updating the totalPayable value in else if, but I'm not able to use the updated value anywhere in the code when I call getOrder(). Is it an API problem or what can anyone help me with the code?
else if (isOrderInitiated == false){
getCleintOrderFromApi();
debugPrint("ifelse" + totalPayable.toString());
}
getClientOrderFromApi() {
orders.clear();
totalPayable = 0.0;
api.getCleintOrder().then((list) {
list.forEach((order) {
if(order.is_placed) {
order.status = ORDER_STATUS[0];
} else if(!order.is_placed) {
order.status = ORDER_STATUS[2];
}
debugPrint("ORDER_STATUS from remote" + order.status);
for (var j = 0; j < order.items.length; j++) {
FoodItemOrder item = order.items[j];
totalPayable = totalPayable + item.unitPrice * item.quantity;
debugPrint(" total payable in order "+ totalPayable.toString());
}
orders.add(order);
debugPrint("itemsin order"+orders.last.items.length.toString());
});
currentOrderList.clear();
currentOrderList.addAll(orders);
orderItemsSink.add(orders);
});
}
}
The debugPrint in getClientOrderFromApi() is showing the updated result, but in else if debugPrint(" ifelse "+ totalPayable.toString()); it is not showing the updated value which is why wherever I use totalPayable it is not showing the desired value.
Future<List<Order>> getCleintOrder() async {
MakeOrder clientOrderRequest = MakeOrder(); //currentCafe;
clientOrderRequest.caffeID = currentCafe.caffeId;
clientOrderRequest.tableidtimestamp = currentUser.tableIdTimestamp;
// offset = 160;
String url = BASE_URL + BASE_URL_GET_CLIENT_ORDER;
debugPrint("requesting getCleintOrder --- \n" +
url +
"\n " +
clientOrderRequest.toClietnOrderJson().toString());
var response = await http
.post(
url,
headers: {
HttpHeaders.contentTypeHeader: 'application/json',
// HttpHeaders.authorizationHeader : ''
},
body: json.encode(clientOrderRequest.toClietnOrderJson()),
)
.catchError(
(error) {
////debugPrint(error.toString());
return false;
},
);
var jsonObj = json.decode(response.body);
////debugPrint(jsonObj.toString());
List<Order> orders = [];
try {
jsonObj.forEach((newJson) {
List<Order> orderList = (newJson["orders"] as List)
.map((neworderJson) => Order.fromJSON(neworderJson))
.toList().cast<Order>();
orders.addAll(orderList);
});
} catch (e) {
////debugPrint(e.toString());
}
// debugPrint("total client orders " + orders.length.toString());
return orders;
}
This is my api call for refernece.

In NetSuite with SuiteScript 2.0 unable to send a file with HTTP POST request with a content-type multipart/form-data

I am not able to send my "multipart/form-data' file to this API. If I use POSTMAN it's working but with the https post method it seems that netsuite doesn't recognize the "form-data" content-type. Somebody knows how to send a form-data with SuiteScript 2 ? Here is a part of my code:
var fileObj = file.create({
name: invoiceNumber + '_ubl.xml',
fileType: file.Type.XMLDOC,
contents: einvoicecontentwithpdf,
folder : 120,
isOnline : false
});
var headers = {
'Authorization': 'Basic xxxxxxxxxxxxxxxxxxxxx',
'Content-Type': 'multipart/form-data'
};
var response = https.post({
url: 'https://community-api-uat-1-2.nxt.uat.unifiedpost.com/api/universal_connector/v1/uc/ar',
body: {
'file': fileObj
},
headers: headers
});
You have to build the whole body yourself.
This works as much as I've tested so far. Code may look a little non-idomatic because it's compiled from Typescript.
Note that to include non-text files this would have to be adapted to base64 encode the file bodies.
/**
* multiPartUpload.js
* #NApiVersion 2.x
*/
define(["require", "exports", "N/https", "N/file", "N/log"], function (require, exports, http, file, log) {
Object.defineProperty(exports, "__esModule", { value: true });
var types = {};
types[file.Type.AUTOCAD] = 'application/x-autocad';
types[file.Type.BMPIMAGE] = 'image/x-xbitmap';
types[file.Type.CSV] = 'text/csv';
types[file.Type.EXCEL] = 'application/vnd.ms-excel';
types[file.Type.FLASH] = 'application/x-shockwave-flash';
types[file.Type.GIFIMAGE] = 'image/gif';
types[file.Type.GZIP] = 'application/?x-?gzip-?compressed';
types[file.Type.HTMLDOC] = 'text/html';
types[file.Type.ICON] = 'image/ico';
types[file.Type.JAVASCRIPT] = 'text/javascript';
types[file.Type.JPGIMAGE] = 'image/jpeg';
types[file.Type.JSON] = 'application/json';
types[file.Type.MESSAGERFC] = 'message/rfc822';
types[file.Type.MP3] = 'audio/mpeg';
types[file.Type.MPEGMOVIE] = 'video/mpeg';
types[file.Type.MSPROJECT] = 'application/vnd.ms-project';
types[file.Type.PDF] = 'application/pdf';
types[file.Type.PJPGIMAGE] = 'image/pjpeg';
types[file.Type.PLAINTEXT] = 'text/plain';
types[file.Type.PNGIMAGE] = 'image/x-png';
types[file.Type.POSTSCRIPT] = 'application/postscript';
types[file.Type.POWERPOINT] = 'application/?vnd.?ms-?powerpoint';
types[file.Type.QUICKTIME] = 'video/quicktime';
types[file.Type.RTF] = 'application/rtf';
types[file.Type.SMS] = 'application/sms';
types[file.Type.STYLESHEET] = 'text/css';
types[file.Type.TIFFIMAGE] = 'image/tiff';
types[file.Type.VISIO] = 'application/vnd.visio';
types[file.Type.WORD] = 'application/msword';
types[file.Type.XMLDOC] = 'text/xml';
types[file.Type.ZIP] = 'application/zip';
function getContentType(f) {
var mime = types[f.fileType];
var charset = f.encoding;
var ct = 'Content-Type: ' + mime + (charset ? ';charset=' + charset : '');
log.debug({ title: 'content for ' + f.name, details: ct });
return ct;
}
function isFile(o) {
return (typeof o == 'object' && typeof o.fileType != 'undefined');
}
/**
* Creates a multipart upload
* #param {string} url to post to
* #param {object} headers key/value of headers; include Auth headers if needed
* #param {array} parts array of {name:string, value:file|string}
*/
function uploadParts(url, headers, parts) {
var boundary = 'someuniqueboundaryasciistring';
headers['content-type'] = 'multipart/form-data; boundary=' + boundary;
// Body
var body = [];
parts.forEach(function (p, idx) {
var partIsFile = isFile(p.value);
body.push('--' + boundary);
body.push('Content-Disposition: form-data; name="' + p.name + '"' + (partIsFile ? ('; filename="' + p.value.name + '"') : ''));
if (partIsFile) {
var ct = getContentType(p.value);
body.push(getContentType(p.value));
if(partIsBinary(ct)){
body.push('Content-Transfer-Encoding: base64');
}
}
body.push('');
body.push(partIsFile ? p.value.getContents() : p.value);
if (idx == parts.length - 1) {
body.push('--' + boundary + '--');
body.push('');
}
});
// Submit Request
try {
var response = http.post({
url: url,
headers: headers,
body: body.join('\r\n')
});
return response;
}
catch (e) {
log.error({ title: 'Failed to submit file', details: (e.message || e.toString()) + (e.getStackTrace ? (' \n \n' + e.getStackTrace().join(' \n')) : '') });
}
}
exports.uploadParts = uploadParts;
;
});
a simple test:
/**
*#NApiVersion 2.x
*#NScriptType Suitelet
*/
define(["require", "exports", "N/file", "./multiPartUpload"], function (require, exports, file, multiPartUpload_1) {
Object.defineProperty(exports, "__esModule", { value: true });
function onRequest(context) {
var files = [
{ name: 'f1', value: file.load({ id: 1056 }) }, // file cabinet ids; you can use dynamic files
{ name:'t1', value:'test'},
{ name: 'f2', value: file.load({ id: 7479 }) }
];
var resp = multiPartUpload_1.uploadParts('https://your-test-server/', {}, files);
context.response.write({ output: resp.body });
}
exports.onRequest = onRequest;
});

Breezejs Extending Entities

I have an issue with Breezejs (1.4.2) q (0.9.7)
I want to add a computed property for an entity.
var doctorInitializer = function (doctor) {
doctor.FullName = ko.computed(function () {
return doctor.FirstName() + " " + doctor.MiddleName() + " " + doctor.LastName() + " " + doctor.SurName();
});
};
var doctorName = '/breeze/polyclinic',
doctorManager = new breeze.EntityManager(doctorName);
var store = doctorManager.metadataStore;
store.registerEntityTypeCtor("Doctor", null, doctorInitializer);
i try adding a knockout computed to the constructor
var doctor = function () {
self.FullName = ko.computed( {
read: function() {
return self.FirstName + " " + self.MiddleName + " " + self.LastName + " " + self.SurName;
},
deferEvaluation: true
});
};
store.registerEntityTypeCtor("Doctor", doctorInitializer);
in both cases only work if i remove the parenthesis but MiddleName and SurName is not required and instead of empty string i got null
this is the error i have http://screencast.com/t/bP9Xnmf9Jm
UPDATE
I try adding the error on console log and follow your example and i have the same error is not a function http://screencast.com/t/bQTyV8XGD0Pk
doctor.FullName = ko.computed(function () {
var fullName = "";
fullName += doctor.FirstName();
if (doctor.FirstName()) {
fullName += ' ' + doctor.FirstName();
}
fullName += ' ' + doctor.LastName();
if (doctor.SurName()) {
fullName += ' ' + doctor.SurName();
}
return fullName;
});
var query = breeze.EntityQuery.from("Doctors").orderBy("Id")
doctorManager.executeQuery(query)
.then(function (data) {
self.doctors.removeAll();
self.doctors(data.results);
})
.fail(function(error) {
console.log(error);
});
I hope someone can help me
The error you are seeing in the screenshot is because your query is throwing an error that you are not handling. Attach a .fail(failFunction) on the end of your entityQuery.
You can't call doctor.Surname() if there is no Surname function that is attached. Calling doctor.Surname just returns a function that doesn't give you a value.
Odds are, you don't 100% get why it's not working because you don't understand how Knockout works. You probably don't yet understand the meaning of what I am describing above either. You need to understand how Knockout works first, then try to learn Breeze.
If you want to just make it work without understand how or why put this in there and continue on. This assumes that there is a property returned called MiddleName and SurName that are just empty.
doctor.FullName = ko.computed(function () {
var fullName = "";
fullName += doctor.FirstName();
if (doctor.MiddleName()) { fullName += ' ' + doctor.MiddleName(); }
fullName += ' ' + doctor.LastName();
if (doctor.SurName()) { fullName += ' ' + doctor.SurName(); }
return fullName
});

"Authorization is required to perform that action" in Google Scripts with no way to give authorization

I am trying to create a sort of middleware so some legacy software I am working on can consume Twitter feeds.
Since Twitter has made API 1.0 obsolete and 1.1 requires OAuth and because for this project I only have client-side scripting available to me, I opted to use a Google script to perform the OAuth negotiation:
Source: http://www.labnol.org/internet/twitter-rss-feeds/27931/
function start() {
// Get your Twitter keys from dev.twitter.com
var CONSUMER_KEY = "-----";
var CONSUMER_SECRET = "----";
// Ignore everything after this line
initialize(CONSUMER_KEY, CONSUMER_SECRET);
}
function initialize(key, secret) {
ScriptProperties.setProperty("TWITTER_CONSUMER_KEY", key);
ScriptProperties.setProperty("TWITTER_CONSUMER_SECRET", secret);
var url = ScriptApp.getService().getUrl();
if (url) {
connectTwitter();
var msg = "";
msg += "Sample RSS Feeds for Twitter\n";
msg += "============================";
msg += "\n\nTwitter Timeline of user #labnol";
msg += "\n" + url + "?action=timeline&q=labnol";
msg += "\n\nTwitter Favorites of user #labnol";
msg += "\n" + url + "?action=favorites&q=labnol";
msg += "\n\nTwitter List labnol/friends-in-india";
msg += "\n" + url + "?action=list&q=labnol/friends-in-india";
msg += "\n\nTwitter Search for New York";
msg += "\n" + url + "?action=search&q=new+york";
msg += "\n\nYou should replace the value of 'q' parameter in the URLs as per requirement.";
msg += "\n\nFor help, please refer to http://www.labnol.org/?p=27931";
MailApp.sendEmail(Session.getActiveUser().getEmail(), "Twitter RSS Feeds", msg);
}
}
function doGet(e) {
var a = e.parameter.action;
var q = e.parameter.q;
var feed;
switch (a) {
case "timeline":
feed = "https://api.twitter.com/1.1/statuses/user_timeline.json?screen_name=" + q;
break;
case "search":
feed = "https://api.twitter.com/1.1/search/tweets.json?q=" + encodeString(q);
break;
case "favorites":
feed = "https://api.twitter.com/1.1/favorites/list.json?screen_name=" + q;
break;
case "list":
var i = q.split("/");
feed = "https://api.twitter.com/1.1/lists/statuses.json?slug=" + i[1] + "&owner_screen_name=" + i[0];
break;
default:
feed = "https://api.twitter.com/1.1/statuses/user_timeline.json";
break;
}
var id = Utilities.base64Encode(feed);
var cache = CacheService.getPublicCache();
var rss = cache.get(id);
if ((!rss) || (rss == "undefined")) {
rss = JSONtoRSS(feed, a, q);
cache.put(id, rss, 3600);
}
return ContentService.createTextOutput(rss)
.setMimeType(ContentService.MimeType.RSS);
}
function JSONtoRSS(json, type, key) {
oAuth();
var options = {
"method": "get",
"oAuthServiceName": "twitter",
"oAuthUseToken": "always"
};
try {
var result = UrlFetchApp.fetch(json, options);
if (result.getResponseCode() === 200) {
var tweets = Utilities.jsonParse(result.getContentText());
if (type == "search")
tweets = tweets.statuses;
if (tweets) {
var len = tweets.length;
var rss = "";
if (len) {
rss = '<?xml version="1.0"?><rss version="2.0">';
rss += ' <channel><title>Twitter ' + type + ': ' + key + '</title>';
rss += ' <link>' + htmlentities(json) + '</link>';
rss += ' <pubDate>' + new Date() + '</pubDate>';
for (var i = 0; i < len; i++) {
var sender = tweets[i].user.screen_name;
var tweet = htmlentities(tweets[i].text);
rss += "<item><title>" + sender + ": " + tweet + "</title>";
rss += " <author>" + tweets[i].user.name + " (#" + sender + ")</author>";
rss += " <pubDate>" + tweets[i].created_at + "</pubDate>";
rss += " <guid isPermaLink='false'>" + tweets[i].id_str + "</guid>";
rss += " <link>https://twitter.com/" + sender + "/statuses/" + tweets[i].id_str + "</link>";
rss += " <description>" + tweet + "</description>";
rss += "</item>";
}
rss += "</channel></rss>";
return rss;
}
}
}
} catch (e) {
Logger.log(e.toString());
}
}
function connectTwitter() {
oAuth();
var search = "https://api.twitter.com/1.1/application/rate_limit_status.json";
var options = {
"method": "get",
"oAuthServiceName": "twitter",
"oAuthUseToken": "always"
};
try {
var result = UrlFetchApp.fetch(search, options);
} catch (e) {
Logger.log(e.toString());
}
}
function encodeString(q) {
var str = encodeURIComponent(q);
str = str.replace(/!/g, '%21');
str = str.replace(/\*/g, '%2A');
str = str.replace(/\(/g, '%28');
str = str.replace(/\)/g, '%29');
str = str.replace(/'/g, '%27');
return str;
}
function htmlentities(str) {
str = str.replace(/&/g, "&");
str = str.replace(/>/g, ">");
str = str.replace(/</g, "<");
str = str.replace(/"/g, """);
str = str.replace(/'/g, "'");
return str;
}
function oAuth() {
var oauthConfig = UrlFetchApp.addOAuthService("twitter");
oauthConfig.setAccessTokenUrl("https://api.twitter.com/oauth/access_token");
oauthConfig.setRequestTokenUrl("https://api.twitter.com/oauth/request_token");
oauthConfig.setAuthorizationUrl("https://api.twitter.com/oauth/authorize");
oauthConfig.setConsumerKey(ScriptProperties.getProperty("TWITTER_CONSUMER_KEY"));
oauthConfig.setConsumerSecret(ScriptProperties.getProperty("TWITTER_CONSUMER_SECRET"));
}
I have followed all of the instructions prescribed in the guide including running 'start' twice... and all that does is send my email account an email with the various URLs. When I try to access the URLs provided in the email (and the plan is to drop that URL into our legacy javascript that currently points to the Twitter 1.0 API), i get the error "Authorization is required to perform that action"
I have confirmed countless times that it is set up to "Execute the app as [me]" and "Anyone, even anonymous can access app"
I am not sure what I am missing or what got screwed up.
Turns out, I forgot to set up the callback URL within the Twitter app setup as specified in the source instructions. Oops!
This would explain why it wasn't working even though everything on the Google side was correct.

Resources