I've just started using hapi.js and have run into a problem when trying to defer the response for a route.
I successfully defer the response for the /query route using this code:
server.route([{
method: 'GET',
path: '/query',
config: {
handler: function(request, reply) {
var response = reply().hold();
db = request.server.plugins['hapi-mongodb'].db;
someFxn(callbackFxn, request, response);
}
}
}]);
var someFxn(cb, req, res){
var raw = {};
//... do lots of stuff that can take a long time
cb(req, res, raw);
}
var callbackFxn = function(request, response, data){
response.source = data;
response.send();
}
The problem I am having is that sometimes someFxn() takes a long time to execute and this seems to perhaps cause a timeout and the request is made again. How can I change the timeout value for the request so that it does not fire the request again?
I really appreciate any advice/leads anyone can give me.
You could be hitting the default Node.js socket timeout of 2 minutes. You can override it using the route config option config.timeout.socket:
From docs:
timeout - define timeouts for processing durations:
...
socket - by default, node
sockets automatically timeout after 2 minutes. Use this option to
override this behavior. Defaults to undefined which leaves the node
default unchanged. Set to false to disable socket timeouts.
Set it like this to disable timeouts entirely for the route:
server.route([{
method: 'GET',
path: '/query',
config: {
timeout: {
socket: false
},
handler: function(request, reply) {
...
}
}
}]);
Related
I am trying to stub out a route using cypress 5.2. I have a simple function to do that:
function mockAlertsResponses(){
cy.route({
method: "GET",
url: "/api/polling/alerts/statistics*",
response: {
total: 2,
...
}
});
}
In one set of tests, this works just fine:
describe("admin and read only permissions", () => {
it ("all action bar items are enabled with admin permissions", () => {
cy.login("user", "password");
mockAlertsResponses();
// Click alert to enable all buttons
cy.get("#1-checkbox").click()
cy.get("#mark-alert-button").should("be.enabled");
cy.get("#delete-alert-button").should("be.enabled");
});
// ... a few more similar tests
});
However, in another set of tests, I use the same function:
it("Logs in and out", () => {
cy.login("username", "password");
mockAlertsResponses()
cy.get("#vine-appbar").should("be.visible");
cy.get("#info-button").click();
cy.get("#info-menu-logout").click();
cy.get("#login-button");
cy.get("#vine-appbar").should("not.be.visible");
});
In this second set of tests, the routes are not stubbing properly, leading to errors and test failures. Here, you can see the route is not stubbed, but rather throws a 500 error:
I'm not sure what could be interfering here. I'm a bit new to cypress and I'm not even sure where to begin debugging this. Might the cy.server calls be interfering? Please let me know what more information we'd need to figure out why one set of stubs works and another doesn't.
If you're wondering, the login function has a call to cy.server and has its own cy.route as well:
Cypress.Commands.add("login", (username, password, admin = true) => {
const response = admin ? <jwt1> : <jwt2>
cy.server();
cy.route({
method: "POST",
url: "/api/login",
response
});
cy.visit("/logout");
cy.visit("/");
cy.wait(100);
cy.get("#login-error").should("not.be.visible");
cy.get("#login-username").type(username);
cy.get("#login-password").type(password);
cy.get("#login-button").click();
});
We're trying to create a trigger in MongoDB Atlas that will automatically reduce our Azure tier of an evening to save cost. The function we've put together (probably incorrectly!) returns an error when we try to run it. The result output is:
logs:
uncaught promise rejection: StitchError: HTTP request failed: unexpected status code: expected=200, actual=415
> result:
{
"$undefined": true
}
> result (JavaScript):
EJSON.parse('{"$undefined":true}')
We've tried messing around with the headers and body, but the end result is always the same.
Here's the WIP function:
exports = function() {
const response = context.http.patch({
scheme: "https",
host: "cloud.mongodb.com",
path: "/api/atlas/v1.0/groups/abc123/clusters/fake-server",
username: "usernamehere",
password: "passwordhere",
digestAuth: true,
headers: {"Content-Type": [ "application/json" ]},
body: {"clusterType":"REPLICASET", "providerSettings":{"providerName":"AZURE", "instanceSizeName":"M10", "regionName":"regionhere" } },
encodeBodyAsJSON: true
});
};
Any help would be greatly appreciated.
It turns out that we had it right the whole time.
Executing the exact same code as above is now correctly reducing our tier. I don't know what was causing the MongoDB Atlas API to think that our headers and / or request body were incorrect, but it's perfectly happy with it today.
The only other thing I will point out is that if you want this part of the error to go away...
> result:
{
"$undefined": true
}
> result (JavaScript):
EJSON.parse('{"$undefined":true}')
...you need to change the function to async / await like so:
exports = async function() {
const response = await context.http.patch({
I am currently stuck with this project : integrating GA to my REST API so I can retrieve metrics and dimensions.
I must highlight that I am running the API on localhost, it may explain why it does not work...?
I tried to integrate GA following different ways :
1. Using universal-analytics (https://github.com/peaksandpies/universal-analytics/blob/master/AcceptableParams.md)
const ua = require('universal-analytics');
require('dotenv').config({path: '../.env'});
// to retrieve the number of connexions to the homepage
server.route({
method: 'GET',
path: '/homepage',
handler: function (request, reply) {
...
const visitor = ua(process.env.UA_ID);
visitor.pageview('/bookmarks', 'http://localhost:4000', 'Homepage', (err) => {
if (err) {
throw err;
}
});
return reply.view('index');
}
});
// I have pretty much the same thing at the end of a POST endpoint handler
// to retrieve the number of new data posted
handler: function (request, reply) {
...
const user = ua(process.env.UA_ID, userId(request.auth.credentials.username));
user.event('Bookmarks', 'new_bkm_created', payload.title, (err) => {
if (err) {
throw err;
}
});
}
2. Creating a trackEvent function (https://cloud.google.com/appengine/docs/flexible/nodejs/integrating-with-analytics)
in google.js
const got = require('got');
module.exports = function trackEvent (propertyId, category, action, options) {
const data = {
v: '1', // API version
tid: propertyId, // Tracking / Property ID.
t: 'event', // Event hit type.
ec: category, // Event category.
ea: action // Event action.
};
if (options) {
if (options.label) {
data.el = options.label; // Event label.
}
if (options.value) {
data.ev = options.value; // Event value.
}
}
return got.post('http://www.google-analytics.com/collect', {
body: data,
form: true
});
};
in app.js
require('dotenv').config({path: '../.env'});
const trackEvent = require('./google');
const postHandler = (request, reply) => {
...
trackEvent(process.env.UA_ID, 'Thing', 'new_thing_created').then(() => {
return reply(bookmark).code(201);
}
}
3. Pasting the website tracking code from Admin --> Property ---> Tracking Info (https://developers.google.com/analytics/devguides/collection/analyticsjs/)
<script>
(function(i,s,o,g,r,a,m)i['GoogleAnalyticsObject']=ri[r]=i[r]||function(){ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;.parentNode.insertBefore(a,m)})(window,document,'script','https://www.google-analytics.com, 'analytics.js','ga');
ga('create', 'UA-XXX-whatev', 'auto');
ga('send', 'pageview');
</script>
I also tried the async version proposed on the link on the third title. The thing is, I do not like this way of doingm I do not understand how to launch events and page views from handlers with ga created in the front end...
Anyway, none of them works... I have no error, but nothing appears in G Analytics.
Just so you know, I used example.com in the default URL as it does not work with a localhost url.
Do you have any lead to help me with this?
Thank you so much in advance!
-- smgr
Turned out one of the three methods worked: the first one with universal-analytics. One needs to be patient to see the first tracked events. I warmly recommend this module though, very developer-friendly ;)
I have this $http request interceptor
app.config(function($httpProvider) {
$httpProvider.interceptors.push(function() {
return {
request: function(req) {
// Set the `Authorization` header for every outgoing HTTP request
req.headers['cdt_app_header'] = 'tamales';
return req;
}
};
});
});
Is there any way we can add a header or cookie to every $http request, but keep the header value secure / not visible with JavaScript?
We can add an obfuscation layer with this header to prevent easy access to our API endpoints, but I am wondering about a more truly secure solution.
Cookies are used for secure sessions, and these are more secure because they cannot be accessed with JavaScript. Say we have a user who can do this request with front-end code:
GET /api/users
we don't really want them to be able to make a simple request with cURL or a browser without an extra piece of information. The cookie we give them will give them the ability to use the browser address bar to make a GET request to /api/users, but if we add the requirement to have another cookie or header in place, then we can prevent them from accessing endpoints that are authorized for, in a format that we don't really want them to use.
In other words, we want to do our best to give them access, but only in the context of a front-end Angular app.
I can't add a comment because of my rep but what are you doing on the back-end to authorize users? If the cookie is signed and contains user permissions it shouldn't matter that the header is visible in the client as it will also be verified on the back-end API call.
in this sample i used HttpRestService to get RESTful API, read this article
at first we create a service to get our configs in this sample is getConfigs
we use getConfigs in the app.run when application is started, after get the configs we set them all in the header as sample.
after that we can get userProfile with new header and also secure by call it from our controller as you see.
in this sample you need to define apiUrl, it's your api host url, remember after logout you can remove the header, also you can define your configs dynamically to make more secure for your application.
HttpRestService.js github link
app.js
var app = angular.module("app", ["HttpRestApp"]);
app.service
app.service("service", ["$http", "$q", "RestService", function (http, q, restService) {
this.getConfigs = function () {
var deferred = q.defer();
http({
method: "GET",
async: true,
headers: {
"Content-Type": "application/json"
},
url: "you url to get configs"
}).then(function (response) {
deferred.resolve(response.data);
}, function (error) {
deferred.resolve(error);
});
return deferred.promise;
}
var api = {
user: "User" //this mean UserController
}
//get user with new header
//this hint to your api with this model "public Get(int id){ return data; }"
//http://localhost:3000/api/users/123456
this.getUserProfile= function(params, then) {
restService.get(params, api.user, true).then(then);
}
}]);
app.run
app.run(["RestService", "service", function (restService, service) {
var header = {
"Content-Type": "application/json"
}
//get your configs and set all in the header
service.getConfigs().then(function (configs) {
header["systemId"] = configs.systemId;
});
var apiUrl = "http://localhost:3000/";
restService.setBaseUrl(apiUrl, header);
}]);
app.controller
app.controller("ctrl", ["$scope", "service", function ($scope, service) {
$scope.getUserProfile = function () {
//this is just sample
service.getUserProfile({ id: 123456 }, function (data) {
$scope.user = data;
});
}
$scope.getUserProfile();
}]);
For some odd reason, iron-router randomly returns undefined.
this.route('pollyShow', {
path: '/polly/:_id',
template: 'polly_show',
notFoundTemplate: 'notFound',
before: function () {
var id = this.params._id;
var poll = Polls.findOne({_id: id});
console.log(poll);
var ip_array = poll.already_voted;
$.getJSON("http://smart-ip.net/geoip-json?callback=?", function(data){
ip_voted = ip_array.indexOf(data.host);
if (ip_voted > -1) {
Router.go('pollyResults', {_id: id});
}
});
},
data: function() {
return Polls.findOne({_id: this.params._id});
}
});
Sometimes it is returning normally while other times it just returns undefined.
Is there any reason behind this?
The problem occurs because the Polly collection is sometimes populated and at other times unpopulated when the route executes.
This problem can be prevented by explicitly waiting on a subscription using waitOn option in the route configuration.
From the docs:
By default, a new Meteor app includes the autopublish and insecure packages, which together mimic the effect of each client having full read/write access to the server's database. These are useful prototyping tools, but typically not appropriate for production applications. When you're ready, just remove the packages.
To remove the packages, call meteor remove <package-name>.
Then you need to explicitly publish records which you want to see on the client on the server:
server/publications.js:
Meteor.publish('all_of_polly', function () { return Polls.find({}); });
And subscribe to it on the client:
this.route('pollyShow', {
path: '/polly/:_id',
template: 'polly_show',
notFoundTemplate: 'notFound',
waitOn: function () { return Meteor.subscribe('all_of_polly'); }
// ...
});