FIXED: SailsJs + Google Blogger is timing out - google-api

So I've been tinkering with SailsJs and really like it so far, but I am trying to pull from posts from a blog I own into a view. This is a bit of a problem because the connection times out when trying to get the index view, and there is no feedback from the console via the console.log entries.
Blogger Service
// BloggerService.js - in api/services
var g = require('googleapis');
var apiKey = 'OUche33eekym0nKEY-uME';
exports.getBlogPosts = function(options, cb) {
g.discover('blogger', 'v3').execute(function(err, client) {
if(err) {
cb = null;
return console.log(err);
} else {
var opts = { 'blogId': options.id, 'maxResults': options.limit, 'fields': 'items(title,content,url)' };
cb = client.blogger.posts.list(opts);
};
});
};
exports.getBlogPost = function(options, cb) {
g.discover('blogger', 'v3').execute(function(err, client) {
if(err) {
cb = null;
return console.log(err);
} else {
var opts = { 'blogId': options.id, 'postId': options.postId };
cb = client.blogger.posts.get(opts);
};
});
};
Calling the service in the controller. Frustrating because the bottom of the documentation has a very cavalier way of saying where/how the service is called.
BlogController.js
/**
* BlogController.js
*
* #description ::
* #docs :: http://sailsjs.org/#!documentation/controllers
*/
module.exports = {
index: function(req, res){
BloggerService.getBlogPosts({'id':'86753098675309','limit':6},function(err, blogPosts){
if(err){
return console.log(err);
} else {
console.log(blogPosts.items[0].url);
res = blogPosts;
};
});
}
}
Index view
<div>
<% _.each(Model.items, function (blogPost) { %>
<div class="panel panel-default">
<div class="panel-heading"><%= blogPost.title %></div>
<div class="panel-body"><%= blogPost.content %><input type="hidden" value="<%= blogPost.id %>"></div>
</div>
<% }) %>
</div>
Any help would be greatly appreciated. Thank you for the time you spent looking at this.
UPDATE
Many thanks to Scott, who got me closer to the end results. This is what I have thus far, but just need to clear up an authentication issue with discover/apiKeys.
exports.getBlogPosts = function(options, cb) {
g.discover('blogger', 'v3').execute(function(err, client) {
if(err) {
cb(err);
} else {
var opts = { 'blogId': options.id, 'maxResults': options.limit, 'fetchBodies': false, 'fields': 'items(title,url)' }
client.blogger.posts.list(opts).withApiKey(apiKey).execute(cb);
};
});
};
exports.getBlogPost = function(options, cb) {
g.discover('blogger', 'v3').execute(function(err, client) {
if(err) {
cb(err);
} else {
var opts = { 'blogId': options.id, 'postId': options.postId };
client.blogger.posts.get(opts).withApiKey(apiKey).execute(cb);
};
});
};

It looks like you might be new to Node.js, so you might want to read up on asynchronous programming with Node and the req and res objects used by Express. The two problems I see with your code right off the bat are:
That you're assigning a value to your callback in BloggerService.js, rather than actually calling the callback: cb = client.blogger.posts.list(opts) should be (based on a quick scan of the Google API docs) client.blogger.posts.list(opts).execute(cb), and in case of an error cb = null should be cb(err).
You're assigning a value to the response object, instead of sending a response: res = blogPosts should be res.json(blogPosts).
As far as where / when you call your service, the docs aren't intending to be cavalier. The services are globalized so that they can be called from anywhere within any controller; it's up to you as the developer to decide where you need your service calls to be!

Related

`next.js` api is resolved before promise fullfill?

I want to achieve something like this:
call my website url https://mywebsite/api/something
then my next.js website api will call external api
get external api data
update external api data to mongodb database one by one
then return respose it's status.
Below code is working correctly correctly. data is updating on mongodb but when I request to my api url it respond me very quickly then it updates data in database.
But I want to first update data in database and then respond me
No matter how much time its take.
Below is my code
export default async function handler(req, res) {
async function updateServer(){
return new Promise(async function(resolve, reject){
const statusArray = [];
const apiUrl = `https://example.com/api`;
const response = await fetch(apiUrl, {headers: { "Content-Type": "application/json" }});
const newsResults = await response.json();
const articles = await newsResults["articles"];
for (let i = 0; i < articles.length; i++) {
const article = articles[i];
try {
insertionData["title"] = article["title"];
insertionData["description"] = article["description"];
MongoClient.connect(mongoUri, async function (error, db) {
if (error) throw error;
const articlesCollection = db.db("database").collection("collectionname");
const customQuery = { url: article["url"] };
const customUpdate = { $set: insertionData };
const customOptions = { upsert: true };
const status = await articlesCollection.updateOne(customQuery,customUpdate,customOptions);
statusArray.push(status);
db.close();
});
} catch (error) {console.log(error);}
}
if(statusArray){
console.log("success", statusArray.length);
resolve(statusArray);
} else {
console.log("error");
reject("reject because no statusArray");
}
});
}
updateServer().then(
function(statusArray){
return res.status(200).json({ "response": "success","statusArray":statusArray }).end();
}
).catch(
function(error){
return res.status(500).json({ "response": "error", }).end();
}
);
}
How to achieve that?
Any suggestions are always welcome!

How to convert into promise and geocode the address with google maps

I want to convert this piece of code that I made into a promise
because I want to show the geocoded address into vuetify
this is my code so far, I'm including google maps api and lodash .
<!DOCTYPE html>
<html>
<head>
<title>Reverse Geocoding Sample</title>
</head>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>
<script>
function geocodelatLng(){
var response = [
{
"address": "213 Marlon Forks\nSouth Corineland, HI 81723-1044",
"lat": "10.30431500",
"lng": "123.89035500"
},
{
"address": "1291 Stephania Road\nLake Dorotheastad, TN 82682-76",
"lat": "10.30309100",
"lng": "123.89154500"
},
{
"address": "20330 Schmeler Course Apt. 210\nNorth Ari, NV 70048",
"lat": "10.30356400",
"lng": "123.89964100"
}
] ;
return _.map(response,coords => {
// console.log(arr.index);
var geocoder = new google.maps.Geocoder;
var latLng = {
lat : parseFloat(coords.lat),
lng : parseFloat(coords.lng)
} ;
// for every lat,lng .
// console.log(latLng);
geocoder.geocode({'location': latLng},function (results,status){
if (status === 'OK') {
if (results[0]) {
console.log(results[0].formatted_address);
} else {
window.alert('No results found');
}
} else {
window.alert('Geocoder failed due to: ' + status);
}
});
});
}
</script>
<script async defer
src="https://maps.googleapis.com/maps/api/js?key=AIzaSyA6vKL6Q4u5ZhGAJlYOMkQZ13pxCUXOe9k&callback=geocodelatLng">
</script>
</body>
</html>
Now this logs everything into the console but my problem is that I dont know how to show it in the v-list-tile-title tag. I tried everything used promises but it won't work, maybe you can help me. Not familiar with es6 tho.
<v-list-tile>
<v-list-tile-content>
<v-list-tile-title>{{ geocodedCoordinates address here }}</v-list-tile-title>
<v-list-tile-sub-title>{{ address.address }}</v-list-tile-sub-title>
</v-list-tile-content>
</v-list-tile>
On the basis that you want geocodelatLng() to return a promise that delivers geocode results, you first need something that will convert geocoder.geocode() from a callback API to Promises. The general principles of doing that are covered extensively here.
Following that advice, you might end up with something like this:
function geocodelatLng() {
var response = [ ....... ]; // as in the question
var promises = response.map(function(coords) { // map response to an array of Promises.
return new Promise(function(resolve, reject) {
var geocoder = new google.maps.Geocoder();
var latLng = {
'lat': parseFloat(coords.lat),
'lng': parseFloat(coords.lng)
};
geocoder.geocode({'location': latLng}, function(results, status) {
if (status === 'OK') {
if (results.length) {
resolve(results[0]); // or `resolve(results)` to deliver all results
} else {
reject(new Error('No results found'));
}
} else {
reject(new Error('Geocoder failed due to: ' + status));
}
});
});
});
return Promise.all(promises);
}
You are left with a possible issue that Promise.all() will return a rejected promise if any one of the promises rejects, which you probably don't want. It would be better to ignore rejections (errors) and deliver successfully derived results.
A solution to that issue is provided here in the form of a reflect function, which can be applied as follows:
function geocodelatLng() {
var response = [ ....... ]; // as in the question
function reflect(promise) {
return promise.then(function(v) {
return { 'status':'resolved', 'value':value };
}, function(e) {
return { 'status':'rejected', 'error':e };
});
}
var promises = response.map(coords => { // map response to an array of Promises.
return new Promise(function(resolve, reject) {
var geocoder = new google.maps.Geocoder();
var latLng = {
'lat': parseFloat(coords.lat),
'lng': parseFloat(coords.lng)
};
geocoder.geocode({'location': latLng}, function(results, status) {
if (status === 'OK') {
if (results[0]) {
resolve(results[0]);
} else {
reject(new Error('No results found'));
}
} else {
reject(new Error('Geocoder failed due to: ' + status));
}
});
});
});
return Promise.all(promises.map(reflect)) // map `promises` to an array of "refelected" promises before passing to Promise.all()
.then(function(results) {
return results.filter(function(res) { // filter the reflected results to exclude errors
return res.status === 'resolved';
}).map(function(res) { // map the remaining reflected results to the value of interest
return res.value;
});
});
}
geocodelatLng's caller is now returned a Promise that will deliver (via .then()) all the successful geocode results, which can be passed to vuetify or whatever.

Is there a soundcloud artwork url scheme like in the facebook api?

I'd like to display the artwork image of a soundcloud track I have the Track-URL from.
I know that when I extract the track key/id from the URL, I can request the artwork URL via their API and then inject the retrieved URL into a image tag.
What I'd like to know is if its possible to use some kind of URL schema to make soundcloud forward browsers to the correct artwork URL like its possible with facebook profile images.
For example:
Mark Zuckerbergs current profile picture has the URL http://profile.ak.fbcdn.net/hprofile-ak-prn2/t5/202896_4_1782288297_q.jpg
Thats some cryptic stuff because its hosted on a CDN. Soundcloud artwork URLs look pretty much cryptic as well.
Now, when I know marks facebook id/key ("zuck"), I can simply access his profile image like so:
http://graph.facebook.com/zuck/picture
That URL is automatically forwarded to the profile picture URL by the facebook API.
Using this URL schema you abstract away not only the reason for a additional API request, but they also safe processing time on their side.
Is there some functionality like this for soundcloud track artworks?
I wrote an express app that redirects to largest available image on their CDN, given artwork_url.
FixSoundCloudArtworkUrl.js
It uses their naming scheme and enumerates sizes one by one until some image returns status 200.
Source:
'use strict';
var express = require('express'),
app = express();
require('./config/development')(app, express);
require('./config/production')(app, express);
var redis = require('redis'),
request = require('request'),
Promise = require('bluebird');
Promise.promisifyAll(redis.RedisClient.prototype);
var redisSettings = app.set('redis'),
redisClient = redis.createClient(redisSettings.port, redisSettings.host, redisSettings.options);
app.configure(function () {
app.use(express.bodyParser());
app.use(app.router);
});
function sendError(res, status, error) {
if (!(error instanceof Error)) {
error = new Error(JSON.stringify(error));
}
return res
.status(status || 500)
.end(error && error.message || 'Internal Server Error');
}
function generateCacheHeaders() {
var maxAge = 3600 * 24 * 365;
return {
'Cache-Control': 'public,max-age=' + maxAge,
'Expires': new Date(Date.now() + (maxAge * 1000)).toUTCString()
};
}
function getCacheKey(url) {
return 'soundcloud-thumbnail-proxy:' + url;
}
app.get('/*', function (req, res) {
var originalUrl = req.params[0],
cacheKey = getCacheKey(originalUrl),
urls;
// https://developers.soundcloud.com/docs/api/reference#artwork_url
// This is a ridiculous naming scheme, by the way.
urls = [
originalUrl,
originalUrl.replace('-large', '-t500x500'),
originalUrl.replace('-large', '-crop'), // 400x400
originalUrl.replace('-large', '-t300x300'),
originalUrl.replace('-large', '-large') // 100x100
];
return redisClient.getAsync(cacheKey).then(function (cachedUrl) {
if (cachedUrl) {
return cachedUrl;
}
return Promise.reduce(urls, function (resolvedUrl, url) {
if (resolvedUrl) {
return resolvedUrl;
}
return new Promise(function (resolve) {
request.head(url, function (err, response) {
if (!err && response.statusCode === 200) {
resolve(url);
} else {
resolve(null);
}
});
});
}, null);
}).then(function (url) {
if (!url) {
throw new Error('File not found');
}
var headers = generateCacheHeaders();
for (var key in headers) {
if (headers.hasOwnProperty(key)) {
res.setHeader(key, headers[key]);
}
}
res.redirect(url);
redisClient.set(cacheKey, url);
redisClient.expire(cacheKey, 60 * 60 * 24 * 30);
}).catch(function (err) {
sendError(res, 404, err);
});
});
app.get('/crossdomain.xml', function (req, res) {
req.setEncoding('utf8');
res.writeHead(200, { 'Content-Type': 'text/xml' });
res.end('<?xml version="1.0" ?><cross-domain-policy><allow-access-from domain="*" /></cross-domain-policy>');
});
redisClient.on('ready', function () {
app.listen(app.set('port'));
});
redisClient.on('error', function () {
throw new Error('Could not connect to Redis');
});
module.exports = app;
No. The only documented way is: API Reference for "/tracks"

How to keep session in multipage angular application?

I am having single page application with user authentication and there is no problem sharing session information there.
However I have part of site where are static pages where I would like just to include session information (logged in user, or login form). How I can share session information between two apps?
I would recommend creating a service that wraps localStorage or other apis to store persistent data. Here is an example using a localStorage implementation.
This implementation is synchronous but if I would use websql like or even server db then I would refactor it to use promises to return the storage object.
Controller
var demo = angular.module('demo', [ 'appStorage' ]);
demo.controller('AppStorageController', [ '$scope', 'appStorage',
function($scope, appStorage) {
appStorage('MyAppStorage', 'myAppStorage', $scope);
} ]);
HTML
<div ng-controller="AppStorageController">
<p>Local Storage: {{myAppStorage}}</p>
<p>
Username: <input type="text" ng-model="myAppStorage.username"></input>
</p>
<p>
Remember me: <input type="checkbox"
ng-model="myAppStorage.rememberMe"></input>
</p>
</div>
JS
angular.module('appStorage', []).factory('appStorage',
[ '$window', function($window) {
var appStorages = {};
var api = undefined;
if ($window.localStorage) {
api = {
set : function(name, value) {
$window.localStorage.setItem(name, JSON.stringify(value));
},
get : function(name) {
var str = $window.localStorage.getItem(name);
var val = {};
try {
val = str ? JSON.parse(str) : {};
}
catch (e) {
console.log('Parse error for localStorage ' + name);
}
return val;
},
clear : function() {
$window.localStorage.clear();
}
};
}
// possibly support other
if (!api) {
throw new Error('Could not find suitable storage');
}
return function(appName, property, scope) {
if (appName === undefined) {
throw new Error('appName is required');
}
var appStorage = appStorages[appName];
var update = function() {
api.set(appName, appStorage);
};
var clear = function() {
api.clear(appName);
};
if (!appStorage) {
appStorage = api.get(appName);
appStorages[appName] = appStorage;
update();
}
var bind = function(property, scope) {
scope[property] = appStorage;
scope.$watch(property, function() {
update();
}, true);
};
if (property !== undefined && scope !== undefined) {
bind(property, scope);
}
return {
get : function(name) {
return appStorage[name];
},
set : function(name, value) {
appStorage[name] = value;
update();
},
clear : clear
};
};
} ]);

How to send ajax request to check session timeout and render relogin message in grails?

I want to display a message to the user saying, "you're logged out re-login please!" when session is timed-out, sending an ajax request each time. If session timer ends i want to send final ajax request displaying above message. But the problem here is i don't know where should i have to keep my ajax and jquery codes and since i don't have much knowledge about ajax request, can anyone explain this process with codes. In siple my requirement is like of what facebook shows on session time out, or when any one tab in case of multiople tabs are logged out. I'm working on grails project.
Do your ajax request like this
$.ajax({
url:url,
type:"POST", // or get
data:parameters,
success: function(data) {
// do procedure if success
}
error : function(xhr, type, error){
// do procedure if fail
// may be send a message to the server side to display a message that shows session timeout
}
});
Handle your session timeout in the error function
I did it myself and this is the js code for it "gracefulSession.js" and call this javascript at the page where you are going to embed your html code..
function checkSessionStatus() {
var lStorage = getLocalStorage();
if (lStorage) {
//lStorage.setItem('poleTime',new Date());
var poleTime = lStorage.getItem("poleTime");
var parsedTime;
try {
parsedTime = new Date(poleTime);
} catch (e) {}
//alert(new Date()-parsedTime)
//alert(new Date())
//alert(parsedTime)
//3900000 = 1H5M
if (parsedTime && (new Date() - parsedTime) < 3900000) {
//alert('NCATCH'+parsedTime);
} else {
//alert('POINT');
poleSessionStatus();
}
}
}
function setlatestPoleTIme() {
//alert("SETTING POLE TIME");
var lStorage = getLocalStorage();
if (lStorage) {
lStorage.setItem('poleTime', new Date());
}
}
function setCheckSessionTimer() {
var lStorage = getLocalStorage();
var isLoggedOut = false;
if (lStorage) {
if (lStorage.getItem('isLoggedOut') == 'true') {
isLoggedOut = true;
}
}
//console.log('checkingIfLoggedOut');
if (!isLoggedOut) {
setTimeout("setCheckSessionTimer();", 5000);
//console.log("NOPT LO");
$('#LoggedoutMessage').hide();
checkSessionStatus();
} else {
setTimeout("setCheckSessionTimer();", 5000);
//console.log("KO");
//alert("You're Logged Out from other tab");
$('#LoggedoutMessage').show();
}
}
function logout() {
//alert("LOGGIN OUT")
var lStorage = getLocalStorage();
if (lStorage) {
lStorage.setItem('isLoggedOut', 'true');
}
}
function resetLoggedOutFLag() {
var lStorage = getLocalStorage();
if (lStorage) {
lStorage.removeItem('isLoggedOut');
}
}
function getLocalStorage() {
var storage, fail, uid;
try {
uid = new Date;
(storage = window.localStorage).setItem(uid, uid);
fail = storage.getItem(uid) != uid;
storage.removeItem(uid);
fail && (storage = false);
} catch (e) {}
return storage
}
Now, HTML code to embed ,
<div id="LoggedoutMessage" style="display:none;position:absolute;background:black;height: 200%;width:100%;top: 0;z-index: 10000;opacity: 0.9;">
<div id="login_box" style="position:fixed;left:38%;top:30%; padding:10px; width: 365px;margin: 0 auto;border: 0px solid #CCC;margin-top: 35px;height: 150px;background: white; border-radius:3px;">
<div id="login_title">
<h1>You have been logged out.</h1>
</div>
<div id="reLogin">
<p>Please login to continue.</p>
<g:link controller="dashBoard" action="index" target="_blank" onclick="logout();">Login</g:link>
</div>
</div>
</div>
Finally where you keep your html,keep this javascript code at top of it embedding script tag:
function poleSessionStatus() {
jQuery.ajax({
type: 'POST',
data: '',
url: '<g:createLink action="ajaxCheckSession" controller="dashBoard"/>',
success: function (data, textStatus) {
//setTimeout ( "checkSession();", 5000);
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
$('#LoggedoutMessage').show();
},
complete: function (XMLHttpRequest, textStatus) {
$.unblockUI();
}
});
}

Resources