Can I get to response headers in Loopback afterRemote hook? - express

I have a Loopback model on which I am logging requests to keen.io using the afterRemote hook. http://docs.strongloop.com/display/public/LB/Remote+hooks#Remotehooks-ctx.result
I am also using the response-time package to add the response time header to the response. https://github.com/expressjs/response-time
This is working fine, expect I cannot figure out how to get to the X-Response-Time header in the response in order to log it to keen.io.
Can I get to the response headers in any way below?
module.exports = function(Studio) {
var isStatic = true;
var isNotStatic = false;
Studio.disableRemoteMethod('deleteById', isStatic); // DELETE /Studios/{id}
Studio.disableRemoteMethod('create', isStatic); // POST /Studios
Studio.disableRemoteMethod('upsert', isStatic); // PUT /Studios
Studio.disableRemoteMethod('updateAll', isStatic); // POST /Studios/update
Studio.disableRemoteMethod('updateAttributes', isNotStatic); // PUT /Studios/{id}
Studio.disableRemoteMethod('__create__ListenNps', isNotStatic);
Studio.disableRemoteMethod('__delete__ListenNps', isNotStatic);
Studio.disableRemoteMethod('__destroyById__ListenNps', isNotStatic);
Studio.disableRemoteMethod('__updateById__ListenNps', isNotStatic);
Studio.afterRemote('*', function(ctx, affectedModelInstance, next) {
var Keen = require('keen-js');
var client = new Keen({
projectId: "myid",
writeKey: "mykey"
});
var queryEvent = {
ip: ctx.req.ip,
baseUrl: ctx.req.baseUrl,
url: ctx.req.url,
route: ctx.req.route,
query: ctx.req.query,
method: ctx.methodString,
// response: ctx.result.???, What can I do here to get to the response headers? Specifically X-Response-Time
keen: {
timestamp: new Date().toISOString()
}
};
client.addEvent("queries", queryEvent, function(err, res) {
if (err) {
console.log(err)
} else {
console.log(res)
}
});
next();
});
};

Try to use ctx.res.getHeader('X-Response-Time') method
or
listen the res.on('finish') event.

Related

Modifying graphql query variable using express-gateway

I'm trying to modify a graphql query variable using express-gateway.
The code on the gateway is as below,
const axios = require("axios");
const jsonParser = require("express").json();
const { PassThrough } = require("stream");
module.exports = {
name: 'gql-transform',
schema: {
... // removed for brevity sakes
},
policy: (actionParams) => {
return (req, res, next) => {
req.egContext.requestStream = new PassThrough();
req.pipe(req.egContext.requestStream);
return jsonParser(req, res, () => {
req.body = JSON.stringify({
...req.body,
variables: {
...req.body.variables,
clientID: '1234'
}
});
console.log(req.body); // "clientID": "1234" is logged in the body.variables successfully here
return next();
});
};
}
};
Now, when I hit the request from POSTMAN, the request goes through and returns a 200OK only when I include clientID, otherwise, it throws as error
"message": "Variable "$clientID" of required type "ID!" was not provided."
Any idea what could be going wrong here?
The only way I could get this working was by using node-fetch and then making a fetch request to the graphql-sever from my middleware instead of doing a return next() and following the middleware chain.
My setup is something like the following,
Client (vue.js w/ apollo-client) ---> Gateway (express-gateway) ---> Graphql (apollo-server) ---> Backend REST API (*)
When my client makes a graphql request to my gateway, I've modified my middleware to do the following (as opposed to what's in the question),
const jsonParser = require("express").json();
const fetch = require('node-fetch');
module.exports = {
name: 'gql-transform',
schema: {
... // removed for brevity sakes
},
policy: () => {
return (req, res) => {
jsonParser(req, res, async () => {
try {
const response = await fetch(`${host}/graphql`, {...}) // removed config from fetch for brevity
res.send(response);
} catch (error) {
res.send({ error });
}
});
};
}
};

Trying to set a cookie established on a web session as a header back to API

I am trying to login via the webfront end and trying to intercept a cookie and then using that in the subsequent API request. I am having trouble getting the cookie back into the GET request. Code posted below.
import https from 'https';
import { bitbucketUser } from "../userRole.js"
import { ClientFunction } from 'testcafe';
fixture `Request/Response API`
// .page `https://myurl.company.com/login`
.beforeEach(async t => {
await t.useRole(bitbucketUser)
});
test('test', async t => {
const getCookie = ClientFunction(() => {
return document.cookie;
});
var mycookie = await getCookie()
const setCookie = ClientFunction(mycookie => {
document.cookie = mycookie;
});
var validatecookie = await getCookie()
console.log(validatecookie)
const executeRequest = () => {
return new Promise(resolve => {
const options = {
hostname: 'myurl.company.com',
path: '/v1/api/policy',
method: 'GET',
headers: {
'accept': 'application/json;charset=UTF-8',
'content-type': 'application/json'
}
};
const req = https.request(options, res => {
console.log('statusCode:', res.statusCode);
console.log('headers:', res.headers);
let body = "";
res.on("data", data => {
body += data;
});
res.on("end", () => {
body = JSON.parse(body);
console.log(body);
});
resolve();
});
req.on('error', e => {
console.error(e);
});
req.end();
});
};
await setCookie(mycookie)
await executeRequest();
});
I have tried several examples but am quite not able to figure what is it that I am missing.
When you call the setCookie method, you modify cookies in your browser using the ClientFunction.
However, when you call your executeRequest method, you run it on the server side using the nodejs library. When you set cookies on the client, this will not affect your request sent from the server side. You need to add cookie information directly to your options object as described in the following thread: How do I create a HTTP Client Request with a cookie?.
In TestCafe v1.20.0 and later, you can send HTTP requests in your tests using the t.request method. You can also use the withCredentials option to attach all cookies to a request.
Please also note that TestCafe also offers a cookie management API to set/get/delete cookies including HTTPOnly.

Dynamic routes in express based on external api data

I need to automatically generate the routes on an expressjs app, based on the vimeo api
I thought that I need to loop through the api data, save the data in the db and then retrieve that data in a middleware. For example:
Api request:
const Vimeo = require("vimeo").Vimeo;
let client = new Vimeo("CLIENT_ID", "CLIENT_SECRET", "TOKEN");
client.request(
{
method: "GET",
path: "/my/path/videos"
},
function(error, body, status_code, headers) {
if (error) {
console.log(error);
}
let data = body.data;
for (var i = 0; i < data.length; i++) {
// save data in the db
}
});
Middleware:
app.use('/videos/:name', (req, res, next) {
if (req.params.name === myDBdata) {
console.log('It works!');
next();
} else {
// error code
}
});
Is this a good way to proceed? Thanks in advance
Make a function which takes two paremter like:
function makeRoute(path, handler) {
return app.use(path, handler)
}
And then call this for every data
makeRoute('test', (req, res) => { })

Test multiple http requests to express application using Jasmine

I have installed Jasmine CLI globally using npm install -g jasmine
I'm trying to test multiple http requests at once using test suite below, multiple calls per each requests were sent (seeing output of console.log() but nothing returned so the test was failure, please guide me is this possible to do so ? and how to do this ?
index.js
var app = require('express')();
var request = require('request');
app.get('/', function(req, res) {
console.log('GET /');
res.status(200);
res.send('Hello World');
});
app.listen(3000);
spec/multipleRequestSpec.js
var request = require('request');
var async = require('async');
describe('express application', function() {
var baseUrl = 'http://localhost:3000';
var statusCode = [0, 0];
var b = ['', ''];
beforeEach(function(done) {
async.parallel([
function() {
request.get(baseUrl, function(err, res, body) {
statusCode[0] = res.statusCode;
b[0] = body;
})
}
,
function() {
request.post(baseUrl, function(err, res, body) {
statusCode[1] = res.statusCode;
b[1] = body;
})
}
], done());
});
it('should return 200', function() {
expect(statusCode[0]).toBe(200);
});
it('should return hello world', function() {
expect(b[0]).toEqual('Hello World');
});
it('should return error 404', function() {
expect(statusCode[1]).toBe(404);
});
});
Edited
When testing only one request I place done() inside the request() it works just fine, but I quite confuse where to place done() when using async.pararell()
spec/requestSpec.js
var request = require('request');
describe('expresss application', function() {
var baseUrl = 'http://localhost:3000';
var statusCode = 0;
beforeEach(function(done) {
request.get(baseUrl, function(err, res, body) {
statusCode = res.statusCode;
done();
});
});
it('should return 200', function() {
expect(statusCode).toBe(200);
});
});
In describe block you initiate variable body. And you use it in it blocks. But in request.get and in request.post you have callback function with parameter body which is in use instead of your describe body variable.
Change beforeEach to:
beforeEach(function(done) {
async.parallel([
function(callback) {
request.get(baseUrl, function(err, res, reqBody) {
statusCode[0] = res.statusCode;
body[0] = reqBody;
callback();
})
}
,
function(callback) {
request.post(baseUrl, function(err, res, reqBody) {
statusCode[1] = res.statusCode;
body[1] = reqBody;
callback();
})
}
], done);
});
I think that you should also check err param in request callbacks. Because there may be errors which fails/pass your tests.
For api endpoints tests it is more easy to use superagent or supertest instead of request.

403 error using node-formidable with expressjs

i've got a problem using node-formidable (https://github.com/felixge/node-formidable) with expressjs: connect-multipart is now deprecated (http://www.senchalabs.org/connect/multipart.html).
I'm trying to use node-formidable to directly parse my uploaded files but can't make it works.
Urlencoded forms are working well but not multipart. I'm not sure but i think that it comes from the connect-csrf:
Update: it works well when i remove the csrf middleware.
Error: Forbidden
at Object.exports.error (/srv/www/mysite.com/nodejs/myapp/node_modules/express/node_modules/connect/lib/utils.js:63:13)
at createToken (/srv/www/mysite.com/nodejs/myapp/node_modules/express/node_modules/connect/lib/middleware/csrf.js:82:55)
at Object.handle (/srv/www/mysite.com/nodejs/myapp/node_modules/express/node_modules/connect/lib/middleware/csrf.js:48:24)
at next (/srv/www/mysite.com/nodejs/myapp/node_modules/express/node_modules/connect/lib/proto.js:193:15)
at next (/srv/www/mysite.com/nodejs/myapp/node_modules/express/node_modules/connect/lib/middleware/session.js:315:9)
at /srv/www/mysite.com/nodejs/myapp/node_modules/express/node_modules/connect/lib/middleware/session.js:339:9
at /srv/www/mysite.com/nodejs/myapp/node_modules/connect-redis/lib/connect-redis.js:101:14
at try_callback (/srv/www/mysite.com/nodejs/myapp/node_modules/connect-redis/node_modules/redis/index.js:581:9)
at RedisClient.return_reply (/srv/www/mysite.com/nodejs/myapp/node_modules/connect-redis/node_modules/redis/index.js:671:13)
at ReplyParser.<anonymous> (/srv/www/mysite.com/nodejs/myapp/node_modules/connect-redis/node_modules/redis/index.js:313:14)
What can i do? Here is my code:
// Body parser
app.use(express.urlencoded());
app.use(function(req, res, next) {
if (req.is('multipart/form-data') && req.method == "POST") {
var form = new formidable.IncomingForm();
form.uploadDir = "mytmpfolder";
form.parse(req, function(err, fields, files) {
req.files = files;
});
}
next();
});
// Cookie parser
app.use(express.cookieParser());
// Session
app.use(express.session({
key: 'secure_session',
store: new redisStore,
secret: 'secret',
proxy: true,
cookie: {
secure: true,
maxAge: null
}
}));
// CSRF
app.use(express.csrf());
app.use(function(req, res, next){
res.locals.token = req.csrfToken();
next();
});
I found a way to finally make it works:
// Body parser
app.use(function(req, res, next) {
if (req.method == "POST") {
var form = new formidable.IncomingForm();
var fieldsObj = {};
var filesObj = {};
form.uploadDir = "/srv/www/mysite.com/nodejs/myapp/static/uploads";
form.on('field', function(field, value) {
fieldsObj[field] = value;
});
form.on('file', function(field, file) {
filesObj[field] = file;
});
form.on('end', function() {
req.body = fieldsObj;
req.files = filesObj;
next();
});
form.parse(req);
}
else {
next();
}
});