Can anyone suggest any ways to get response times from Axios? I've found axios-timing but I don't really like it (controversial, I know). I'm just wondering if anyone else has found some good ways to log response times.
You can use the interceptor concept of axios.
Request interceptor will set startTime
axios.interceptors.request.use(function (config) {
config.metadata = { startTime: new Date()}
return config;
}, function (error) {
return Promise.reject(error);
});
Response interceptor will set endTime & calculate the duration
axios.interceptors.response.use(function (response) {
response.config.metadata.endTime = new Date()
response.duration = response.config.metadata.endTime - response.config.metadata.startTime
return response;
}, function (error) {
error.config.metadata.endTime = new Date();
error.duration = error.config.metadata.endTime - error.config.metadata.startTime;
return Promise.reject(error);
});
This is my solution, by setting the header in the interceptor:
import axios from 'axios'
const url = 'https://example.com'
const instance = axios.create()
instance.interceptors.request.use((config) => {
config.headers['request-startTime'] = process.hrtime()
return config
})
instance.interceptors.response.use((response) => {
const start = response.config.headers['request-startTime']
const end = process.hrtime(start)
const milliseconds = Math.round((end[0] * 1000) + (end[1] / 1000000))
response.headers['request-duration'] = milliseconds
return response
})
instance.get(url).then((response) => {
console.log(response.headers['request-duration'])
}).catch((error) => {
console.error(`Error`)
})
Here's another way to do it:
const instance = axios.create()
instance.interceptors.request.use((config) => {
config.headers['request-startTime'] = new Date().getTime();
return config
})
instance.interceptors.response.use((response) => {
const currentTime = new Date().getTime()
const startTime = response.config.headers['request-startTime']
response.headers['request-duration'] = currentTime - startTime
return response
})
instance.get('https://example.com')
.then((response) => {
console.log(response.headers['request-duration'])
}).catch((error) => {
console.error(`Error`)
})
piggybacking off of #user3653268- I modified their answer to use with react hooks and display x.xxx seconds using a modulo.
import React, { useState } from 'react';
import axios from 'axios';
export default function Main() {
const [axiosTimer, setAxiosTimer] = useState('');
const handleSubmit = () => {
let startTime = Date.now();
axios.post('urlstuff')
.then(response => {
console.log('handleSubmit response: ', response);
axiosTimerFunc(startTime);
})
.catch(err => {
console.log("handleSubmit error:", err.response.data.message)
axiosTimerFunc(startTime);
setLoading(false);
});
}
const axiosTimerFunc = (startTime) => {
let now = Date.now();
let seconds = Math.floor((now - startTime)/1000);
let milliseconds = Math.floor((now - startTime)%1000);
setAxiosTimer(`${seconds}.${milliseconds} seconds`);
}
return(
<div>
<h2>Load Time: {axiosTimer}</h2>
</div>
)
}
easy way do this with async \ await, but not ideal :
const start = Date.now()
await axios.get(url)
const finish = Date.now()
const time = (finish - start) / 1000
This would be time about of axios call. Not so ideal, but showing and easy to implement
Another simple way to do it :
axios.interceptors.response.use(
(response) => {
console.timeEnd(response.config.url);
return response;
},
(error) => {
console.timeEnd(error.response.config.url);
return Promise.reject(error);
}
);
axios.interceptors.request.use(
function (config) {
console.time(config.url );
return config;
}, function (error) {
return Promise.reject(error);
});
Its way long after but this is my simple workaround
function performSearch() {
var start = Date.now();
var url='http://example.com';
var query='hello';
axios.post(url,{'par1':query})
.then(function (res) {
var millis = Date.now() - start;
$('.timer').html(""+Math.floor(millis/1000)+"s")
})
.catch(function (res) {
console.log(res)
})
}
this is my workaround
actually you can get it through the "x-response-time" header that you get from the response on the axios request
axios({
method: 'GET',
url: 'something.com',
})
.then((response) => {
console.log(response.headers['x-response-time']);
})
Related
I am trying to do http post in protractor. The status of http.post sits in pending status and doesn't return any response.
I am calling a method in onPrepare function under specDone:
jasmine.getEnv().addReporter({
specDone: function(result) {
if (result.status == "failed") {
browser.getCapabilities().then(function(caps) {
var browserName = caps.get("browserName");
browser.takeScreenshot().then(function(png) {
var stream = fs.createWriteStream(
"./reports/screenshots/" +
browserName +
"-" +
result.fullName +
".png"
);
stream.write(new Buffer(png, "base64"));
stream.end();
});
});
}
new PortalData().PushDataToPortal("");
}
});
Below function in called from onPrepare, the API takes the parameter from body. I am using protractor-http-client package for API calls.
export class PortalData {
public PushDataToPortal(result) {
const http: HttpClient = new HttpClient();
const LogFilePathInSharedLocation =
"\\\\10.101.101.11\\temp\\DocStar\\Automation\\TestLogs\\Logs.txt";
http
.post(
someurl,
LogFilePathInSharedLocation,
{ "Content-Type": "application/x-www-form-urlencoded" }
)
.then((response: ResponsePromise) => {
console.log(response);
});
}
Please advise. Thanks!
I would suggest you use Http call in beforeAll instead of onPrepare.
You can try superagent or supertest npm modules:
Example for superagent
const superagent = require('superagent');
// callback
superagent
.post('/api/pet')
.send({ name: 'Manny', species: 'cat' }) // sends a JSON post body
.set('X-API-Key', 'foobar')
.set('accept', 'json')
.end((err, res) => {
// Calling the end function will send the request
});
// promise with then/catch
superagent.post('/api/pet').then(console.log).catch(console.error);
// promise with async/await
(async () => {
try {
const res = await superagent.post('/api/pet');
console.log(res);
} catch (err) {
console.error(err);
}
})();
Example for supertest
const supertest = require('supertest');
const request = supertest(`${baseURL}`);
request.put('/test/sendlocal')
.send(profileAddressData.createData)
.set('Content-Type', 'application/json')
.set('Accept', '*/*')
.expect(200)
.end((err, res) => {
if (err) {
console.error('Error: ', err);
console.error('Response: ', res);
}
});
I'm using puppeteer to scrape page that has contents that change periodically and use express to present data in rest api.
If I turn on headless chrome to see what is being shown in the browser, the new data is there, but the data is not showing up in get() and http://localhost:3005/api-weather. The normal browser only shows the original data.
const express = require('express');
const server = new express();
const cors = require('cors');
const morgan = require('morgan');
const puppeteer = require('puppeteer');
server.use(morgan('combined'));
server.use(
cors({
allowHeaders: ['sessionId', 'Content-Type'],
exposedHeaders: ['sessionId'],
origin: '*',
methods: 'GET, HEAD, PUT, PATCH, POST, DELETE',
preflightContinue: false
})
);
const WEATHER_URL = 'https://forecast.weather.gov/MapClick.php?lat=40.793588904953985&lon=-73.95738513173298';
const hazard_url2 = `file://C:/Users/xdevtran/Documents/vshome/wc_api/weather-forecast-nohazard.html`;
(async () => {
try {
const browser = await puppeteer.launch({ headless: true });
const page = await browser.newPage();
await page.setRequestInterception(true);
page.on("request", request => {
console.log(request.url());
request.continue();
});
await page.goto(hazard_url2, { timeout: 0, waitUntil: 'networkidle0' });
hazard = {
"HazardTitle": "stub",
"Hazardhref": "stub"
}
let forecast = await page.evaluate(() => {
try {
let forecasts = document.querySelectorAll("#detailed-forecast-body.panel-body")[0].children;
let weather = [];
for (var i = 0, element; element = forecasts[i]; i++) {
period = element.querySelector("div.forecast-label").textContent;
forecast = element.querySelector("div.forecast-text").textContent;
weather.push(
{
period,
forecast
}
)
}
return weather;
} catch (err) {
console.log('error in evaluate: ', err);
res.end();
}
}).catch(err => {
console.log('err.message :', err.message);
});
weather = forecast;
server.get('/api-weather', (req, res) => {
try {
res.end(JSON.stringify(weather, null, ' '));
console.log(weather);
} catch (err) {
console.log('failure: ', err);
res.sendStatus(500);
res.end();
return;
}
});
} catch (err) {
console.log('caught error :', err);
}
browser.close();
})();
server.listen(3005, () => {
console.log('http://localhost:3005/api-weather');
});
I've tried several solutions WaitUntil, WaitFor, .then and sleep but nothing seems to work.
I wonder if it has something to do with express get()? I'm using res.end() instead of res.send() is because the json looks better when I use res.end(). I don't really know the distinction.
I'm also open to using this reload solution. But I received errors and didn't use it.
I also tried waitForNavigation(), but I don't know how it works, either.
Maybe I'm using the wrong search term to find the solution. Could anyone point me in the right direction? Thank you.
I want to combine the results of 2 queries and then return them as one, like this:
test: async (req, res) => {
const valOne = TableOne.find({ id: id })
.exec((err, result) => {
if (err) {
res.serverError(err);
}
return result;
});
const valTwo = TableTwo.find({ id: id })
.exec((err, result) => {
if (err) {
res.serverError(err);
}
return result;
});
const data = {
keyOne: valOne,
keyTwo: valTwo,
};
res.json(data);
}
I understand above code won't return because it's async. How can I achieve this?
There is not much info you supply: node version, sails version, etc.
There are several approaches here:
1. Using promises
2. Using callback chaining
3. Using await/async
If you use sails 1.0 and node >= 8, your best bet is to use await/async, so your code should work like that:
test: async (req, res) => {
let valOne, valTwo;
try {
valOne = await TableOne.find({ id: id });
valTwo = await TableTwo.find({ id: id });
} catch (err) {
return res.serverError(err); //or res.badRequest(err);
}
const data = {
keyOne: valOne,
keyTwo: valTwo,
};
res.json(data);
}
i am creating an upload function that will show a progress bar to the client inside a React Redux and Redux-observable, and i use axios to do a put request to AWS S3.
My epics is as follow
...
function uploadFile(mimetype, url, file) {
const config = {
headers: {
'Content-Type': mimetype,
},
onUploadProgress(progress) {
const percentCompleted = Math.round((progress.loaded * 100) / progress.total)
uploadProgress(percentCompleted)
},
}
axiosRetry(axios, { retries: 3 })
return axios.put(url, file[0], config)
}
export const uploadEpic = (action$, store) => action$
.ofType(SIGNED_URL_SUCCESS)
.mergeMap(() => {
const file = store.getState().File.droppedFile
const mimetype = file[0].type
const { url } = store.getState().SignedUrl
const { fileData } = store.getState().Upload
return of(uploadFile(mimetype, url.data, file))
.concatMap(() => {
const uploadedData = {
url: fileData.url,
thumbUrl: `${fileData.folder}/${fileData.filename}-00001.png`,
}
return [
upload(uploadedData),
uploadSuccess(),
]
})
.catch(error => of(uploadFailure(error)))
})
export default uploadEpic
The upload seems to work, as i received an AWS SNS email telling that its done, but i can't seem to see that it is updating the Upload.progress state inside my Upload reducer.
The reason i am using axios is particulary because its axios-retry and its onUploadProgress, since i can't seem to find an example doing an onProgress using universal-rx-request
so two questions probably
How can i achieve this using axios
How can i achieve this using universal-rx-request
Thanks to this SO answer
I ended up not using axios at all
I got it working with this
import { of } from 'rxjs/observable/of'
import { Subject } from 'rxjs/Subject'
import { Observable } from 'rxjs/Observable'
import 'rxjs/add/observable/dom/ajax'
import { SIGNED_URL_SUCCESS } from 'ducks/SignedUrl'
import {
upload,
uploadIsLoading,
uploadSuccess,
uploadFailure,
uploadProgress,
} from 'ducks/Upload'
export const uploadEpic = (action$, store) => action$
.ofType(SIGNED_URL_SUCCESS)
.mergeMap(() => {
const file = store.getState().File.droppedFile
const mimetype = file[0].type
const { url } = store.getState().SignedUrl
const { fileData } = store.getState().Upload
const progressSubscriber = new Subject()
const request = Observable.ajax({
method: 'PUT',
url: url.data,
body: file[0],
headers: {
'Content-Type': mimetype,
},
progressSubscriber,
})
const requestObservable = request
.concatMap(() => {
const uploadedData = {
...
}
return [
upload(uploadedData),
uploadIsLoading(false),
uploadSuccess(),
]
})
.catch(error => of(uploadFailure(error)))
return progressSubscriber
.map(e => ({ percentage: (e.loaded / e.total) * 100 }))
.map(data => uploadProgress(data.percentage))
.merge(requestObservable)
})
UPDATE: on rxjs 6 the merge operators is deprecated, so if you're using rxjs 6, change the code above to
// some/lib/folder/uploader.js
import { of, merge } from 'rxjs' // import merge here
import { ajax } from 'rxjs/ajax'
import { map, catchError } from 'rxjs/operators' // instead of here
import { Subject } from 'rxjs/Subject'
export function storageUploader(...args) {
const progressSubscriber = new Subject()
const request = ajax({...someRequestOptions})
.pipe(
map(() => success()),
catchError((error) => of(failure(error))),
)
const subscriber = progressSubscriber
.pipe(
map((e) => ({ percentage: (e.loaded / e.total) * 100 })),
map((upload) => progress(upload.percentage)),
catchError((error) => of(failure(error))),
)
return merge(subscriber, request) // merge both like this, instead of chaining the request on progressSubscriber
}
//the_epic.js
export function uploadEpic(action$, state$) {
return action$
.pipe(
ofType(UPLOAD),
mergeMap((someUploadOptions) => uploaderLib(
{ ...someUploadOptions },
actionSuccess,
actionFailure,
actionProgress,
)),
catchError((error) => of(actionFailure(error))),
)
}
I am new to testing and having difficulty with Sinon stubs and mocks.
How can I write test for 'quote.list_quote' for following senario.
Here is the routes file : quotes.js
const express = require('express');
const request = require('request');
const async = require('async');
const validator = require('validator');
const quote_router = express.Router();
const confg = require("../../confg/confg");
const quote = require("../models/mquotes");
const quotes_model = quote.quotes;
// host name - needs to be set up using the environment variable
const hostname = confg.hostname;
// route for "quotes/"
quote_router.route("/")
// get route : display the random quote
.get((req, res) => {
// display random quote
async.waterfall([
(callback) => {callback(null, {res});},
quote.count_quotes
], check_quote_exist
);
})
// post route : create a new quote
.post((req, res) => {
const doc_json = {author : validator.escape(req.body.quote_author), quote_text : validator.escape(req.body.quote_text)};
const params = {res, doc_json, quote_action : quote.create_quote};
add_edit_quote(params);
})
// put route : edit the quote
.put((req, res) => {
const doc_json = {author : validator.escape(req.body.quote_author), quote_text : validator.escape(req.body.quote_text)};
const params = {res, doc_json, quote_action : quote.update_quote, qid : req.body.quote_id};
add_edit_quote(params);
})
// delete quote : delete the quote
.delete((req, res) => {
const qid = req.body.qid;
const condition = {_id : qid};
async.waterfall([
(callback) => {callback(null, {res, condition});},
quote.delete_quote
], request_quote_list
);
});
// route for "quotes/list" : display quotes list
quote_router.get("/list/", (req, res) => {
// mention the main operation
let operation;
if(req.body.operation != 'undefined') {
operation = req.body.operation;
} else {
operation = "list_quotes";
}
async.waterfall([
(callback) => {callback(null, {res, operation});},
quote.list_quote
], display_quotes_list
);
});
// display the quotes list
const display_quotes_list = (err, params, quotes_list) => {
if (err) {return console.log(err);}
const res = params.res;
const operation = params.operation;
const header_msg = "List of all the quotes";
let alert_msg;
if(operation == "list_quotes") {
alert_msg = null;
} else if(operation == "delete_quote") {
alert_msg = "Quote has been deleted";
}
const params_out = {
page: "quote_list",
title: 'Quotes Manager',
host: hostname,
header_msg,
alert_msg,
quotes_list
};
res.render('index', params_out);
};
// send http request for quote list page
const request_quote_list = (err, params) => {
if (err) {return console.log(err);}
const res = params.res;
const operation = "delete_quote";
request.get('http://' + hostname + '/quotes/list/', {json:{operation}},
(error, response, body) => {
if (!error && response.statusCode == 200) {
res.send(body);
}
});
};
module.exports = quote_router;
This is not complete file. I have included only a portion of it.
And her is the model file : mquotes.js
const mongoose = require('mongoose');
// Define quote schema
const quoteSchema = new mongoose.Schema({
author: String,
quote_text: {type: String, required: true}
},
{timestamps: true}
);
const quote = {};
// Define quotes model
quote.quotes = mongoose.model('quotes', quoteSchema);
// error handler
error_handler = (callback, err, params, return_value) => {
if(err) { return callback(err);}
else {callback(null, params, return_value);}
};
// add quote - create
quote.create_quote = (params, callback) => {
const res = params.res;
const doc_json = params.doc_json;
quote.quotes.create(doc_json, (err, quotes_detail) => {
error_handler(callback, err, {res, operation : 'create_quote'}, quotes_detail);
});
};
// count the number of quotes
quote.count_quotes = (params, callback) => {
quote.quotes.count({}, (err, quotes_count) => {
error_handler(callback, err, params, quotes_count);
});
};
// delete quote - delete - id
quote.delete_quote = (params, callback) => {
quote.quotes.remove(params.condition, (err, query) => {
error_handler(callback, err, params);
});
};
// list quote - find
quote.list_quote = (params, callback) => {
quote.quotes.find({}, (err, quotes_list) => {
error_handler(callback, err, params, quotes_list);
});
};
// find quote by id
quote.quote_by_id = (params, callback) => {
quote.quotes.findById(params.qid, (err, quotes_detail) => {
error_handler(callback, err, params, quotes_detail);
});
};
// returns the detail of random quote
quote.random_qoute = (params, callback) => {
const random_number = params.random_number;
// select one quote after skipping random_number of times
quote.quotes.findOne({}, (err, quotes_detail) => {
error_handler(callback, err, params, quotes_detail);
}).skip(random_number);
};
// update quote - update - id
quote.update_quote = (params, callback) => {
const options = {new: true};
const qid = params.qid;
const update_json = params.doc_json;
quote.quotes.findByIdAndUpdate(qid, {$set: update_json}, options, (err, quotes_detail) => {
params.operation = 'update_quote';
error_handler(callback, err, params, quotes_detail);
});
};
module.exports = quote;
I have installed mocha globally. Now, I want to test the model. Lets take the quote.list_quote function for example.
const mongoose = require('mongoose');
const chai = require('chai');
const sinon = require('sinon');
const expect = chai.expect; // use the "expect" style of Chai
const mquotes = require('./../../app/models/mquotes');
describe('Tests for quote models', () => {
describe("List quote", () => {
it('list_quote() should return list of quotes', () => {
});
});
});
Can anyone tell me about my coding practice too. I mean the way I use functions and modules.
First of all, you should try to use statics methods. And after that, you should use sinon-mongoose and sinon-as-promised if you want to use Promise in mongoose.
And this is my sample code and test with mocha, chai, and sinon. Hope useful for you.
model.js
var Schema = new mongoose.Schema({
name: String,
created_at: {
type: Date,
default: Date.now
},
updated_at: {
type: Date,
default: Date.now
}
});
Schema.statics.findByName = function(name, cb) {
this.findOne({
name: name
})
.exec()
.then(function getTemplate(template) {
if (!template) {
var error = new Error('Not found template by name: "' + name + '"');
error.status = 404;
return cb(error);
}
return cb(null, template);
})
.catch(function catchErrorWhenFindByTemplateName(error) {
error.status = 500;
return cb(error);
});
}
module.exports = mongoose.model('model', Schema);
test.js
var expect = require('chai').expect,
sinon = require('sinon'),
mongoose = require('mongoose');
require('sinon-as-promised');
require('sinon-mongoose');
var Model = require('../../app/models/model');
describe('Model', function () {
describe('static methods', function () {
describe('#findByName', function () {
var ModelMock;
beforeEach(function () {
ModelMock = sinon.mock(Model);
});
afterEach(function () {
ModelMock.restore();
});
it('should get error status 404 if not found template', function (done) {
var name = 'temp';
ModelMock
.expects('findOne').withArgs({name: name})
.chain('exec')
.resolves(null);
Model.findByName(name, function (error) {
expect(error.status).to.eql(404);
ModelMock.verify();
done();
});
});
it('should get message not found template if name is not existed', function (done) {
var name = 'temp';
ModelMock
.expects('findOne').withArgs({name: name})
.chain('exec')
.resolves(null);
Model.findByName(name, function (error) {
expect(error.message).to.match(/Not found template by name/gi);
ModelMock.verify();
done();
});
});
it('should get template when name is existed', function (done) {
var name = 'temp';
ModelMock
.expects('findOne').withArgs({name: name})
.chain('exec')
.resolves('SUCCESS');
Model.findByName(name, function (error) {
expect(error).to.be.null;
ModelMock.verify();
done();
});
});
it('should get error status 500 when model crashed', function (done) {
var name = 'temp';
ModelMock
.expects('findOne').withArgs({name: name})
.chain('exec')
.rejects(new Error('Oops! Crashed'));
Model.findByName(name, function (error) {
expect(error.status).to.eql(500);
ModelMock.verify();
done();
});
});
});
});
});