Backbone: special encoding during save - backbone.js

Note: I know this is wrong, but this is a technical requirement by the server team.
I have a User object that extends Backbone.Model. It receives it's data using normal, mostly good, JSON from the server.
HOWEVER there is a requirement when saving THE SAME INFORMATION to encode emails with url encoding.
When receiving the data it is possible to pre-process it with the Backbone.Model.parse method, is there an equivalent way to pre-process the data before sending it? (without overriding the sync method)

I overrode Backbone.sync to change the format of the data to form encoded. However, with this method saving attributes that are arrays becomes a problem.
There's probably a better way to override sync, but I added this code to around line 1180 of the backbone.js file.
//convert to form encoded
if (Backbone.sendFormEncoded) {
if (type === 'PUT' || type === 'DELETE' || type === 'POST') {
params.contentType = 'application/x-www-form-urlencoded';
var kvps = [], regEx = /%20/g;
var obj = model.toJSON();
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
if(obj[key]) {
kvps.push(encodeURIComponent(key).replace(regEx, "+") + "=" + encodeURIComponent(obj[key].toString()).replace(regEx, "+"));
} else {
kvps.push(encodeURIComponent(key).replace(regEx, "+") + "=" + encodeURIComponent(obj[key]));
}
}
}
params.data = kvps ? kvps.join('&') : {};
}
}
Be sure to set Backbone.sendFormEncoded to true for this block of code to run.

Related

Trying to call an API using a trigger and get data

I am very new at this and I am trying my best to work it out. It would be glad if someone can really save my day.
Trying to call an API using a trigger and get data using google scripts this is the best I came up with.
Basically I have an api which gives me objects if I putit in Postman. All I need to do it get those object in google spreadsheets.
I have tried onOpen function which was giving me an error of" You do not have permission to call Fetch".
So have been trying with on edit of first column as "Get Data" so the api can be called.
Thanks in advance
function onEdit(e) {
var s = SpreadsheetApp.getActiveSheet();
if (s.getName() == "Sheet1") { //checks that we're on the correct sheet
var r = s.getActiveCell();
//Updates timestamp for lead entry and meetings.
var currentCellValue = r.getValue();
var user = Session.getEffectiveUser();
if (r.getColumn() == 1 && (currentCellValue == 'Get Data')) { //checks the column
var nextCell = r.offset(0, 4);
if (nextCell.getValue() == '') { //is empty?
nextCell.setValue(Utilities.formatDate(new Date(), "GMT+5:30", "dd/MM/yyyy HH:mm:ss")).setComment(user);
var url = "My API";
var headers = {
"Content-Type": "application/json",
"Authorization": "userkey"
};
var options = {"method":"GET","headers": headers
};
var response = UrlFetchApp.fetch(url, options);
Logger.log(response[0]);
}
}
}
}
This appears to be a long standing issue that still hasn't been taken care of, see here
Someone in the thread mentions
But, using a custom function name like myOnEdit(), and connecting it
to the onEdit event manually, totally works!
function myOnEdit(e) { ... var x = fetchUrlApp.fetch(...); ... }
So you have to create myOnEdit and then put an onEdit trigger on it, either via Resources > Current Project's triggers or via the trigger service.

Download a file in Angular Environment

I need to download a file from the server. The file is stored in the database. I have a cs controller that serves a file back to UI. The server GET call looks like this:
http://server/api/controllername/fileid/data
It does work when I run that link in the Browser - the file comes down and goes into the download area (Chrome). But when I send the same command from my Angualar code I dont see any file. The console reports that my request was successful (code 200), but I just dont see anything. Please let me know what code fragments to post to make it easier to help.
Thanks
Create a link to the resource, and don't handle it with ajax.
If you make the link open in a new tab, the tab will automatically close after it realises it was just opened to download a file in most modern browsers.
Try this code:
var a = document.createElement('a');
a.href = "http://server/api/controllername/fileid/data";
a.click();
You can compose the address concatenating variables and text.
The file probably downloads correctly as a byte[] to the calling it but that would be useless to the user - as was my problem.
In my case I needed to download a file with a complex set of parameters. This example JavaScript uses a post request and creates a form (and posts it) with any JavaScript object that you give it. This code may help if you simplified it:
private open(verb, url, data, target)
{
var form = document.createElement("form");
form.action = url;
form.method = verb;
form.target = target || "_self";
if (data) {
this.createFormParameters(form, "", data);
}
form.style.display = 'none';
document.body.appendChild(form);
form.submit();
}
private createFormParameters(form, key, value) {
// recursive algorithm to add parameters (including objects and arrays of objects) to a custom form
if (typeof value === "object") {
for (var item in value) {
if (Array.isArray(value[item])) {
for (var arrayItem in value[item]) {
this.createFormParameters(form, (item + "[" + arrayItem + "]."), value[item][arrayItem]);
}
continue;
}
var input = document.createElement("textarea");
input.name = key + item;
input.value = value[item];
form.appendChild(input);
}
}
else
{
var input = document.createElement("textarea");
input.name = key;
input.value = value;
form.appendChild(input);
}
}

How to pass query param to parse-rest-api from angularjs $http service?

I'm learning AngularJS , i set-up a development environment using sublime-text as editor and parse.com-rest-api used as back-end layer.
I came across a scenario where, I have to fetch data based on an attribute.
Below given code from the service layer of angularjs has fetch all records from table 'filim'.
var config = {
headers: {
'X-Parse-Application-Id': 'bNtp8FUfr0s1UsAwJr7MFjabCI31HytIuC3gCaJ2',
'X-Parse-REST-API-Key': 'g18cAoH7QkrBZenPqH0pynMKsn6pj4MyfDyIy6X1',
}
};
return {
getFilims: function(callback) {
var filims;
var resp = $http.get('https://api.parse.com/1/classes/filim', config).success(function(data) {
callback(data.results);
});
}
}
I have modified above url to send query-parameter to filter the output, but did not work.
I refer parse.com api doc [ https://www.parse.com/docs/rest#queries ] to modify url to send query - param.
Modified code is given below,
var params = {"where": {"status" : "CLOSED" } }
var resp = $http.get('https://api.parse.com/1/classes/filim?%s' % params, config).success(function(data) {
callback(data.results);
});
But this did not work.
Is this the way to use query-parameter ?
Regards
Ajil
'https://api.parse.com/1/classes/filim?%s' % params
This is a python pattern for interpolating strings and will not work in javascript.
The correct way of combining strings in javascript is:
'https://api.parse.com/1/classes/filim?' + params
Even still, that will probably not work because you'll end up with something like:
'https://api.parse.com/1/classes/filim?[Object object]
What you need to do for parse.com is to JSON encode the query, so try this:
var whereQuery = {"status" : "CLOSED"};
var url = 'https://api.parse.com/1/classes/filim?where=' + encodeURI(JSON.stringify(whereQuery));
var resp = $http.get(url, config).success(function(data) {
callback(data.results);
});

Custom (OData) routes in Restangular

On the server side, I'm using Web API with the OData routing convention, which means that my route for getting a single entity looks something like this:
/api/v1/Products(1)
rather than:
/api/v1/Products/1
Normally, in Restangular, I'd be able to get a single entity with something like this:
Restangular.one('Product', 1);
But that doesn't work for my OData endpoint. I've looked at customGET, and setRequestInterceptor but I can't seem to find an example of or figure out how to change the route to match my endpoint. Preferably globally since all of my entities will have this same format.
Any help is greatly appreciated.
Restangular documentation details how to create a custom configuration, you could do the same by editing the source restangular.js but this extensibility point allows us to keep a clean implementation that should be compatible with most customisations or future versions of RestAngular as well as allowing side-by-side standard REST APIs and OData v4 APIs.
How to create a Restangular service with a different configuration from the global one
// Global configuration
app.config(function(RestangularProvider) {
RestangularProvider.setBaseUrl('http://localhost:16486');
RestangularProvider.setRestangularFields({ id: 'Id' });
});
// Restangular service targeting OData v4 on a the specified route
app.factory('ODataRestangular', function(Restangular) {
return Restangular.withConfig(function(RestangularConfigurer) {
RestangularConfigurer.setBaseUrl(RestangularConfigurer.baseUrl + '/odata');
// OData v4 controller(key) Item Route convention
RestangularConfigurer.urlCreatorFactory.path.prototype.base = function(current) {
var __this = this;
return _.reduce(this.parentsArray(current), function(acum, elem) {
var elemUrl;
var elemSelfLink = RestangularConfigurer.getUrlFromElem(elem);
if (elemSelfLink) {
if (RestangularConfigurer.isAbsoluteUrl(elemSelfLink)) {
return elemSelfLink;
} else {
elemUrl = elemSelfLink;
}
} else {
elemUrl = elem[RestangularConfigurer.restangularFields.route];
if (elem[RestangularConfigurer.restangularFields.restangularCollection]) {
var ids = elem[RestangularConfigurer.restangularFields.ids];
if (ids) {
// Crude Implementation of 'several', don't try this with more than
// 60 Ids, performance degrades exponentially for large lists of ids.
elemUrl += '?$filter=((Id eq ' + ids.join(')or(Id eq ') + '))';
}
} else {
var elemId;
if (RestangularConfigurer.useCannonicalId) {
elemId = RestangularConfigurer.getCannonicalIdFromElem(elem);
} else {
elemId = RestangularConfigurer.getIdFromElem(elem);
}
if (RestangularConfigurer.isValidId(elemId) && !elem.singleOne) {
elemUrl += '(' + (RestangularConfigurer.encodeIds ? encodeURIComponent(elemId) : elemId) + ')';
}
}
}
acum = acum.replace(/\/$/, '') + '/' + elemUrl;
return __this.normalizeUrl(acum);
}, RestangularConfigurer.baseUrl);
};
// add a response interceptor for OData v4:
RestangularConfigurer.addResponseInterceptor(function(data, operation, what, url, response, deferred) {
var extractedData;
// Collection requests are 'getList' operations
if (operation === "getList") {
// return the value array
extractedData = data.value;
} else {
// return the first item in the array
if(data.value.length > 0)
extractedData = data.value[0];
}
// pass the metadata back
if(extractedData) {
extractedData.meta = { context: data['#odata.context'] };
if(data['#odata.count'])
extractedData.meta.count = data['#odata.count'];
}
return extractedData;
});
});
});
Implementation example:
// Controller for list route
function ListCtrl($scope, ODataRestangular) {
$scope.providers = ODataRestangular.all("providers").getList({ $count:true }).$object;
$scope.some = ODataRestangular.several("providers", 15,16,17,18).getList();
$scope.single = ODataRestangular.one("providers", 15).get();
}
Captured URLs from network Traffic:
http://localhost:16486/odata/providers?$count=true
http://localhost:16486/odata/providers?$filter=((Id eq 15)or(Id eq 16)or(Id eq 17)or(Id eq 18))
http://localhost:16486/odata/providers(15)
I struggled to try to write a custom service factory and to modify BreezeJS to work with OData v4 and only recently stumbled into Restangular, I can now really appreciate the extensible design that went into restangular, the general lack of documented client side framework support has been the Achilles heel that has prevented a wider adoption of OData v4. I hope this answer contributes to getting more developers onboard with version 4.
Restangular does not explicitly support OData APIs. You can make the basics work, but you would probably be better off using a library that does support querying an OData API, like breeze.js.

Backbone.js parse for saving

I have a model which keeps some other models in an attribute array. When these models are stored, however, I don't want to keep the sub-modules around--instead, I want to store the primary keys, and then when the model is fetched from the server, its parse will "reconstitute" them by fetching the related models.
What is the best approach to accomplishing this? The closest I've come to getting it to work is overriding the sync method:
sync : function(method, model, options) {
var topics = this.get('topics');
model.attributes.topics = _.pluck(topics, 'id');
var ret = Backbone.Model.prototype.sync.call(this, method, model, options);
this.attributes.topics = topics;
return ret;
},
but this regularly fails, leaving the keys in the attributes instead of the full models & consequently crashing.
Parse function (slightly paraphrased):
parse : function(response) {
response.topics = _.map(response.topics, function(item) {
return app.topics.getByPK(item);
}
return response;
}
What I would do would be something more along these lines:
parse : function(response) {
this.topics = _.map(response.topics, function(item) {
return app.topics.getByPK(item);
}
return response;
}
Which keeps your array of ids intact at all times, and you have access by using this.topics instead of this.get('topics') or this.attributes.topics

Resources