localStorage undefined in rendr controller - javascript

Im getting 500 ReferenceError: localStorage is not defined in the controller of my Rendr app. Im trying to fetch my Authorization token from localStorage and set it as a header before I fetch the spec. I've also tried window.localStorage but then I get window is not defined. Do I not have access to the window object in the controller level? If not, how would I fetch from localStorage.
This is my code for the controller.
module.exports = {
show: function(params, callback) {
var spec = {
model: {
model: 'Company', params: { name: params.id }
}
};
var options = {},
Authorization = localStorage.getItem('Authorization');
options.header = {
"Authorization": Authorization
}
this.app.fetch(spec, options, function (err, results) {
// return if there is an error fetching the user
if (err) return callback(err);
// set the title of the page to the users name
this.app.set('title', results.model.get('name'));
// render the page with the results from the fetch
callback(null, results);
}.bind(this));
}
};

Welcome to Rendr :-)
Rendr is Isomorphic (or "Universal), which means a lot of it's code runs both on the server AND in the browser. If you have code that you only want to run on the browser there are two ways to make that happen:
In the views there is a custom method called postRender - that method is not run on the server, and only runs on the browser. It's the standard place to put all of your browser specific code. The downside is that it is run after the page is rendered.
You can wrap the code in if (window !== 'undefined') {...} to ensure that it only runs in a browser. The downside is that it will never run on the server.
In our Rendr app, we do a bit of using localstorage, and kinda have to wedge it into the very top of the base template. It's a bit weird because the concepts of localstorage (the browser has persistence) fight the concepts of isomorpic apps (the server and the browser can be the same). So they don't work together great.

Related

Inconsistently getting 'FirebaseError: Response is not valid JSON object.'

I'm working on a react-native app with spotify integration. I've set up the oAuth flow w/ auth code grant where I can get the authorization code. I've then set up cloud function on firebase to proxy the actual token exchange (I don't want to reveal my secret to the client!). I've added logs and can see that the function is correctly completing the exchange with the spotify token endpoint, and receiving a refresh and access token.
const tokenRequeset = functions.https.onCall(async (data, context) => {
// spotify network request, error handling, etc here ....
// I want to emphasize that this network request completes
// properly - my log statement below verifies in server logs
// that I'm getting the expected value.
const resp = await axios.post(
"https://accounts.spotify.com/api/token",
QueryString.stringify({
grant_type: "authorization_code",
code: code,
redirect_uri: redirectURI,
}),
{
headers: {
"Authorization": `Basic ${BEARER_TOKEN}`,
"Content-Type": "application/x-www-form-urlencoded",
},
}
);
console.log(resp.data.access_token);
return { status: "success", token: resp.data.access_token };
});
export default tokenRequest
resp.data.access_token is the JWT access token used to hit the spotify API - it's a string value according to the API. (I'd provide an example one, but it is an auth token)
However, when I try to use the firebase/functions package to call my function from my app, I will sometimes get a 'FirebaseError: Response is not valid JSON object.'
What makes this extra fun is that it's inconsistent - yesterday I had the issue, and then it went away (without changing my code!). I was able to hit both the local emulator function and then the deployed function no problem, but today the 'FirebaseError: Response is not valid JSON object.' error is back.
I have checked the logs for the failed invocations both locally and on the deployed function, and in both cases the spotify API call is working - I'm getting all the expected behavior right up until the return (which isn't working for some reason).
On the client side, I'm configuring firebase like so:
const firebaseConfig = {
// Shhhhhh
};
// Initialize Firebase
const app = initializeApp(firebaseConfig);
const functions = getFunctions(app);
// Uncomment to run locally
connectFunctionsEmulator(functions, "localhost", 5001);
export { app, functions };
And then exposing and calling it like so:
const SpotifyAuth = httpsCallable(functions, "spotify-auth");
const resp = await SpotifyAuth(code, redirectURI)
(I know this isn't full code - I grabbed the relevant portions. Happy to provide more if needed).
I tried looking up this error, and I found results from ~2018/2020 with the old style of firebase/functions, but they seem to be related to region and I'm deployed in the default us-central1 - according to the SDK documentation that means I shouldn't touch it.
The existing solutions to the problem also seem to be based on the old style of function calls, rather than the more recent httpsCallable() and getFunctions(app).
I'm going insane trying to figure out why sometimes I'm getting this error
EDIT:
More information on the error - I ran my code again today and didn't see the error locally, but I DID see it when I hit the deployed function.
Again, I want to emphasize that I think the error is in the firebase network response - if you look at the network request I receive a 200 but the response is empty.
Did an additional full mockup of a function to see what would happen:
const test = functions.https.onCall((data, context) => {
console.log("function call");
return { status: "success", token: "asdfasdfasdfasdfasfs" };
});
export default test;
I'm getting the same error.
UPDATE:
I've given up on using the sdk and onCall method for firebase cloud functions - all of my testing thus far indicates that this is a bug or error on the google cloud function side, and there's nothing I can do from my side.
The good news is the onRequest approach seems to not have this issue - it's behaving properly and reliably.
I really hope that I've messed up along the way and there's a solution I've missed - the SDK seems fantastic and I like the integration it (is supposed to) offer, but as far as I'm aware right now unless there's a bug fix (or update to the documentation if I'm doing something wrong) it seems like it simply won't work.
I'm still planning on using firebase, but from my experience thus far I'd advise anyone early in their server work to consider using another offering (at least if you need to use the functions - I was able to get storage working).

Custom error message in Google Apps Script Error object [duplicate]

This is a doPost function inside a Google App that returns a Hello World message.
function doPost(e){
return ContentService.createTextOutput('Hello World');
}
Now suppose I want to only accept valid JSON to be posted to this Google App endpoint and I want to send a respones with Bad Request status. How can I do that. Here's the pseudo code:
function doPost(e){
try{
const data = JSON.parse(e.postData.contents);
return ContentService.createTextOutput('Hello World');
}catch(err){
// Send Bad Request
}
}
Issue and workaround:
Unfortunately, in the current stage, ContentService cannot modify the status code. When I saw the official document of Class ContentService, such method cannot be found. Ref It seems that this is the current specification.
So in your situation, as the current workaround, how about returning the value as JSON data? By this, you can check the value using the key of JSON data. For example, how about the following sample script?
When the correct value without the error is returned,
return ContentService.createTextOutput(JSON.stringify({value: 'value'}));
When the value with the error is returned,
return ContentService.createTextOutput(JSON.stringify({error: 'Error message'}));
When you need .setMimeType(ContentService.MimeType.JSON), please add this.
Note:
When I searched about this at the Google issue tracker, I couldn't find it. So how about reporting this as the future request? Ref
Reference:
Class ContentService
Here's another workaround that allows raising errors on the client side for errors on the web app side. For example, a client might need to catch errors such as bad url args sent to the web app (i.e. the OP's question), or catch errors thrown by a method that is called from doGet() or doPost().
As far as I know, when an error is thrown downstream of doGet() or doPost(), a text error message is returned in the response, but the web app request itself succeeds, so there is no error thrown on the client side. As #Tanaike said, there still seems no way for a Google web app dev to throw an HTTP error from the app (like 400 Bad Request or 500 Internal Server Error).
The idea involves returning a function body from the web app, which the client can use to create and run a dynamic function via the Function() constructor (this assumes Javascript is available on the client).
So the web app can be written to:
return a function body that will throw an error for bad args, server method errors, etc.
return a function body that will return intended JSON when there is no error
This is a bit of a hack, but it unifies error handling on the client side. The client makes the http request, constructs a function using the function body returned in the response, and then runs this function, all in one try{} block. Then both Google-raised http errors and web app downstream errors can be caught in the catch{} block.
Example setup for a Google Apps Script client making a request to a Google web app:
(1) In the web app doGet() or doPost() function:
// this string will be returned by the webapp
var fnBody;
// for bad url args, return a fnBody that will throw an error with an indicative message
if(!urlArgsOk()) {
fnBody = "'use strict'; throw new Error('POST args error');";
}
// if url args are ok, call server method
else {
try {
// if the method call succeeds, return a fnBody that will return the intended JSON
var returnObj = myServerMethod(methodArgs);
fnBody = "'use strict'; return JSON.stringify(" + JSON.stringify(returnObj) + ");";
}
catch(serverErr) {
// if the method call fails, return a fnBody that will throw an error ...
// ... simple example shown here, but info from serverErr can be included in fnBody
fnBody = "'use strict'; throw new Error('server error');";
}
}
// return fnBody, which can be run via Function() on the client
return ContentService.createTextOutput(fnBody).setMimeType(ContentService.MimeType.TEXT);
(2) On the client side (Google apps script client making a POST request)
// Set the url, payload, and fetch options
var url = "https://script.google.com/_______/exec?arg1=val1&arg2=val2";
var payload = getPayloadString(); // whatever POST payload needs to be sent
var options = {
'method' : 'POST',
'contentType': 'application/json',
'muteHttpExceptions': false, // let Google http exceptions come through
'payload' : payload,
'headers': {authorization: "Bearer " + ScriptApp.getOAuthToken()}
};
// Send a request to the web app
try {
// make the POST request - this throws Google-generated HTTP errors if any
var response = UrlFetchApp.fetch(url, options);
// create the dynamic function from the fnBody returned
var responseFn = new Function(response.getContentText());
// run the function - this returns intended JSON content
// or throws web app downstream errors if any
var responseJson = responseFn();
}
catch(err) {
// handle either source of error
console.log(err.message);
}
There are potential security risks associated with dynamic code, so I'm not sure how widely applicable this might be. I might use this in an aplication that lives entirely in a private GCP domain, i.e. with the web app restricted to same-domain users and the client app also in the same domain. Some security is also added by the 'use strict' directive, which boxes the dynamic function in by setting its this to undefined (ref). But it's still a good idea to think through the dynamic code implications (ref1, ref2).

send http request to angular js

I know how to send an http request to a server using angular js. With the promise returned, I know how to listen for a response and manipulate the ui thereafter. But this approach cannot be used for what I have in mind.
However, what I cannot figure out, is how to send a request to a website.
I have a server localhost:800/receiveData which receives a POST request and then manipulate the UI and DoM on the angularjs site
app.get('/', function(req,res){
res.sendFile(__dirname+'/index.html')
})
app.post('/receiveData', function(req,res){
var data = req.body.data
// assume data is a boolean
if(data){
//show a view in index.html using angular js or anything else
}else {
//show a different view in index.html
}
});
Any help will be greatly appreciated. I have a need for angular js. Having a SPA is imperative. I am completely open to adding additional stacks if neccessary.
EDIT:
As pointed out by MarcoS, manipulation of dom should ideally not happen from the server side. I am combining IPFS with node js and angular js to develop a single page application. The swarm of nodes set up using IPFS has an open line of communication with my server (by design). Based on packets of data sent via the comm line to my server, I need to convey messages to the user via the index.html.
I think your approach is wrong: on server-side, you should NOT manipulate the UI and DOM...
You should just do server activity (update a database, send an email, ..., return a static page).
Then you can output a result (JSON/XML/... format) for your client-side calling script to read.
Following OP edit, what I do understand is he wants server push to the client.
To get serve side pushes, you should poll on the client.
In a controller:
function getServerState(changeState) {
return $http.get("/receiveData").then(function(res) {
changeState(res.data); // notify the watcher
}).catch(function(e) {
/* handle errors here */
}).then(function() {
return getServerState(changeState); // poll again when done call
});
}
Consuming it this way:
getServerState(function(status) {
$scope.foo = status; // changes to `foo` $scope variable will reflect instantly on the client
});
And, server side:
app.post('/receiveData', function(req, res) {
var data = req.body.data; // assume data is a boolean
res.end(JSON.stringify(data);
});

Storing KnockoutJS modeled data with AmplifyJS

I'm trying to figure out a way to cache my knockoutJS SPA data and I've been experimenting with amplifyJS. Here's one of my GET functions:
UserController.prototype.getUsers = function() {
var self = this;
return $.ajax({
type: 'GET',
url: self.Config.api + 'users'
}).done(function(data) {
self.usersArr(ko.utils.arrayMap(data.users, function(item) {
// run each item through model
return new self.Model.User(item);
}));
}).fail(function(data) {
// failed
});
};
Here's the same function, "amplified":
UserController.prototype.getUsers = function() {
var self = this;
if (amplify.store('users')) {
self.usersArr(ko.utils.arrayMap(amplify.store('users'), function(item) {
// run each item through model
return new self.Model.User(item);
}));
} else {
return $.ajax({
type: 'GET',
url: self.Config.api + 'users'
}).done(function(data) {
self.usersArr(ko.utils.arrayMap(data.users, function(item) {
// run each item through model
return new self.Model.User(item);
}));
}).fail(function(data) {
// failed
});
};
This works as expected, but I'm not sure about the approach I used, because it will also require extra work on the addUser, removeUser and editUser functions. And seeing as I have many more similar functions throughout my app, I'd like to avoid the extra code if possible.
I've found a way of handling things with the help of ko.extenders, like so:
this.usersArr = ko.observableArray().extend({ localStore: 'users' });
Then use the ko.extenders.localStore function to update the local storage data whenever it detects a change inside the observableArray. So on init it will write to the observableArray in case local storage data exists for users key and on changes it will update the local storage data.
My problem with this approach is that I need to run my data through the model and I couldn't find a way to do that from the localStore function, which is kept on a separate page.
Has any of you worked with KO and Amplify? What approach did you use? Should I use the first one or try a combination of the two and rewrite the extender in a way that it only updates the local storage without writing to the observableArray on init?
Following the discussion in the question's comments, I suggested to use native HTTP caching instead of adding another caching layer on the client by means of an extra library.
This would require implementing a conditional request scheme.
Such a scheme relies on freshness information in the Ajax response headers via the Last-Modified (or E-Tag) HTTP headers and other headers that influence browser caching (like Cache-Control: with its various options).
The browser transparently sends an If-Modified-Since (or If-None-Match) header to the server when the same resource (URL) is requested subsequently.
The server can respond with HTTP 304 Not Modified if the client's information is still up-to-date. This can be a lot faster than re-creating a full response from scratch.
From the Ajax request's point of view (jQuery or otherwise) a response works the same way, no matter if it actually came from the server or if it came from the browser's cache, the latter is only a lot faster.
Carefully adapting the server side is necessary for this, the client side on the other hand does not need much change.
The benefit of implementing conditional requests is reduced load on the server and faster response behavior on the client.
A specialty of Knockout to improve this even further:
If you happen to use the mapping plugin to map raw server data to a complex view model, you can define - as part of the options that control the mapping process - a key function. Its purpose is to match parts of your view model against parts of the source data.
This way parts of the data that already have been mapped will not be mapped again, the others are updated. That can help reduce the client's processing time for data it already has and, potentially, unnecessary screen updates as well.

AngularJS: Cancel overwriting values $resource object after calling save ();

var User = $resource(
'http://test/index.php'
);
var user = User.get({id:'1'});
// GET: http://test/index.php?id=1
// server returns: { "login":"foo", "name":"bar", "mail":"baz" }
user.name = "qux";
user.$save();
// POST: http://test/index.php?id=1
// server returns: { "login":"foo", "name":"bar", "mail":"qux"}
In this case, when you call the save() user object, properties will be replaced by those that came from the server.
But if the server responds like this:
{
"errors":{
"login":"too short",
"name":"is already using that name.",
"mail":"invalid email."
}
}
User object properties are overwritten and instead, property errors containing these mistakes will come up.
Is there a way to change the behavior of $resource? I would like to check the status of the response and, based on that, decide whether to update the properties of an object or report an error to the user.
Angular's $resource is meant to interact with RESTful web services.
In RESTful web services, if there's an error while saving a resource, you should return an appropriate HTTP status (for example, 400).
Then, you can optionally use the error callback:
user.$save(function (response) {
console.log("success!");
}, function (response) {
console.log("error");
});
For a full list of error HTTP statuses:
http://en.wikipedia.org/wiki/List_of_HTTP_status_codes#4xx_Client_Error

Categories