I wrote some nodejs codes to be evaluated by a particular test file, candidate.test.js but surprisingly the test keeps failing. Below is the error I am getting:
Error: expect(received).toEqual(expected) // deep equality
Expected: 201
Received: 500
I have below the snippet from both app.js and candidate.test.js.
candidate.test.js
const request = require('supertest');
const app = require('../app');
// ...
async function makeRegularGetRequest(requestBody) {
return await request(app)
.post('/restaurant')
.send(requestBody)
.expect('Content-Type', /json/);
}
it('should create a restaurant given valid parameters', async () => {
const requestBody = {
name: '123',
position: 200,
category: 'Pizza',
rating: 5,
};
const createResponse = await makeRegularPostRequest(requestBody);
try {
expect(createResponse.status).toEqual(201);
} catch (error) {
throw error;
}
});
// ...
app.js
// ...
app.post('/restaurant', (request, response) => {
const id = generateRandomString();
const { name, position, category, rating } = request.body;
const restaurant = {
name: name,
position: position,
category: category,
rating: Number(rating),
id: id,
};
restaurants.push(restaurant);
response.send(restaurant).status(201);
});
// ...
I also tried sending only the statusCode to the page using reponse.send(restaurant).status(201).statusCode, but the error still comes out.
I tested this on Postman and it works really fine but it's strange that the test does not pass.
can you console log before and after pushing to restaurants array
Related
I'm trying to make GET request to external API (Rick and Morty API). The objective is setting a GET request for unique character, for example "Character with id=3". At the moment my endpoint is:
Routes file:
import CharacterController from '../controllers/character_controller'
const routes = app.Router()
routes.get('/:id', new CharacterController().get)
export default routes
Controller file:
async get (req, res) {
try {
const { id } = req.params
const oneChar = await axios.get(`https://rickandmortyapi.com/api/character/${id}`)
const filteredOneChar = oneChar.data.results.map((item) => {
return {
name: item.name,
status: item.status,
species: item.species,
origin: item.origin.name
}
})
console.log(filteredOneChar)
return super.Success(res, { message: 'Successfully GET Char request response', data: filteredOneChar })
} catch (err) {
console.log(err)
}
}
The purpose of map function is to retrieve only specific Character data fields.
But the code above doesn't work. Please let me know any suggestions, thanks!
First of all I don't know why your controller is a class. Revert that and export your function like so:
const axios = require('axios');
// getCharacter is more descriptive than "get" I would suggest naming
// your functions with more descriptive text
exports.getCharacter = async (req, res) => {
Then in your routes file you can easily import it and attach it to your route handler:
const { getCharacter } = require('../controllers/character_controller');
index.get('/:id', getCharacter);
Your routes imports also seem off, why are you creating a new Router from app? You should be calling:
const express = require('express');
const routes = express.Router();
next go back to your controller. Your logic was all off, if you checked the api you would notice that the character/:id endpoint responds with 1 character so .results doesnt exist. The following will give you what you're looking for.
exports.getCharacter = async (req, res) => {
try {
const { id } = req.params;
const oneChar = await axios.get(
`https://rickandmortyapi.com/api/character/${id}`
);
console.log(oneChar.data);
// return name, status, species, and origin keys from oneChar
const { name, status, species, origin } = oneChar.data;
const filteredData = Object.assign({}, { name, status, species, origin });
res.send(filteredData);
} catch (err) {
return res.status(400).json({ message: err.message });
}
};
const { response } = require("response");
const express = require("express");
const https = require("https")
const app = express ();
app.get("/", function(req,res) {
const url = "https://api.openweathermap.org/data/2.5/weather?q=London&applied=536bcef96b2f01cd9b9f076db90807fe&unit=metric";
https.get(url, function(response) {
console.log(response.statusCode);
})
response.on("data", function(data) {
const weatherData = JSON.parse(data)
console.log(weatherData);
})
res.send("Welcome to the future");
})
app.listen(3000, function() {
console.log("listening on port 3000");
})
The problem here is that when I type
response.on to get data from the url
to print it in the command line, it
brings const { response } = require
("express") as shown above which is
very alien to me.
Please, how do I fix it so I can get
my weatherData printed in the CMD?
There are quite a few things you'll need to change.
First, this section is wrong:
https.get(url, function(response) {
console.log(response.statusCode);
})
response.on("data", function(data) {
const weatherData = JSON.parse(data)
console.log(weatherData);
})
Since "response" is a parameter you receive from the callback on the "get" function, you need to declare the "response.on" inside the funcion scope, like this:
https.get(url, function(response) {
console.log(response.statusCode);
response.on("data", function(data) {
const weatherData = JSON.parse(data)
console.log(weatherData);
})
})
Also, the "data" event only delivers a chunk of data. You should be listening for an "end" event aswell, and only parse the data when you receive the "end" event
https.get(url, function(response) {
console.log(response.statusCode);
const result = []
response.on("data", function(data) {
result.push(data);
})
.on("end", function() {
const weatherData = JSON.parse(result.join(""));
console.log(weatherData);
})
})
And since you're not using the module named "response", you also need to remove this:
const { response } = require("response");
And then correct all the typos that were already mentioned in the comments, which were:
Add the missing quote " at require("express) on line 2
Remove the extra backsting at console.log("listening on port 3000")`; on line 17
Change the second query parameter on your URL on line 6 from "applied" to "appid"
First confirm your url is valid
https://api.openweathermap.org/data/2.5/weather?q=London&appid=536bcef96b2f01cd9b9f076db90807fe&unit=metric
If you are using windows 10 or 11 you don't need all those responses simply try this at cmd line (NOTE you need for each & within the url to escape with ^ like this ^&)
curl -o current.txt https://api.openweathermap.org/data/2.5/weather?q=London^&appid=536bcef96b2f01cd9b9f076db90807fe^&unit=metric
type current.txt
you could include both in one line but for the &type that last & does not need ^escape
curl -o current.txt https://api.openweathermap.org/data/2.5/weather?q=London^&appid=536bcef96b2f01cd9b9f076db90807fe^&unit=metric&type current.txt
after the download you should see the response in the console.
so you can call that command any way you wish (hidden or not) and or read the text file on the screen or in any application you choose.
Current.txt
{"coord":{"lon":-0.1257,"lat":51.5085},"weather":[{"id":800,"main":"Clear","description":"clear sky","icon":"01n"}],"base":"stations","main":{"temp":276.78,"feels_like":272.23,"temp_min":275.76,"temp_max":278.14,"pressure":999,"humidity":84},"visibility":10000,"wind":{"speed":6.17,"deg":250},"clouds":{"all":9},"dt":1641696366,"sys":{"type":2,"id":2019646,"country":"GB","sunrise":1641715415,"sunset":1641744666},"timezone":0,"id":2643743,"name":"London","cod":200}
Here's a fairly simple version using the got() library for making the http request. It's a promise-based, higher level library that is just a lot easier to use than the https library which works at a lower level and requires more code to work properly and handle errors.
Here's how you would do this with the got() library:
const got = require("got");
const express = require("express");
const app = express();
app.get("/", async function(req, res) {
try {
const url = "https://api.openweathermap.org/data/2.5/weather?q=London&appid=536bcef96b2f01cd9b9f076db90807fe&unit=metric";
const result = await got(url).json();
console.log(result);
res.json(result);
} catch (e) {
console.log(e);
res.sendStatus(500);
}
});
app.listen(3000, function() {
console.log("listening on port 3000");
});
Changes:
Fixed URL (change applied to appid).
Switch to got() library for the http request and built in JSON parsing
Add error handling
Send result as JSON
This generates the following output:
{
coord: { lon: -0.1257, lat: 51.5085 },
weather: [ { id: 800, main: 'Clear', description: 'clear sky', icon: '01n' } ],
base: 'stations',
main: {
temp: 276,
feels_like: 271.47,
temp_min: 274.33,
temp_max: 277.49,
pressure: 1000,
humidity: 86
},
visibility: 10000,
wind: { speed: 5.66, deg: 250 },
clouds: { all: 8 },
dt: 1641707384,
sys: {
type: 2,
id: 2019646,
country: 'GB',
sunrise: 1641715415,
sunset: 1641744666
},
timezone: 0,
id: 2643743,
name: 'London',
cod: 200
}
I am using mongoose in Nodejs with typescript. In a list request, I have the following code:
async list(req: Request, res: Response, next: NextFunction)
{
try {
let list: ListInterface[] = []
await RequestModel.find().then(requests =>
{
requests.map(async request =>
{
const client = await Client.findById(request.cliente)
const seller = await Seller.findById(request.vendedor)
const company = await Company.findById(request.representada)
const tmp =
{
id: request._id,
data: request.data,
cliente: String(client?.nome_fantasia),
vendedor: String(seller?.nome),
representada: String(company?.nome_fantasia),
tipo: request.tipo,
status: request.status
}
list.push(tmp)
console.log(tmp)
})
})
console.log(list)
return res.json(list)
} catch (error) {
next(error)
}
}
When I make the request in Insomnia, I receive an empty array (both in the terminal -- because of console.log(list) -- and on Insomnia's preview): []. In the terminal, as a result of the console.log(tmp) command, I receive the proper data.
I have already tried to declare the list as something like const list = request.map(...), but it gives me [Promise<Pending>, Promise<Pending>] in the terminal and [{}, {}] in Insomnia. I could not repeat exactly the same code of this test, but I remember those results.
I'm not even sure about which technology I'm misusing. Could someone please help me with this problem?
.map function returns a list of promises, so have to wait for them before sending back the response.
async list(req: Request, res: Response, next: NextFunction)
{
try {
let list: ListInterface[] = []
const requests = await RequestModel.find()
// collect all promises from the loop
const promises = requests.map(async request =>
{
const client = await Client.findById(request.cliente)
const seller = await Seller.findById(request.vendedor)
const company = await Company.findById(request.representada)
const tmp =
{
id: request._id,
data: request.data,
cliente: String(client?.nome_fantasia),
vendedor: String(seller?.nome),
representada: String(company?.nome_fantasia),
tipo: request.tipo,
status: request.status
}
list.push(tmp)
console.log(tmp)
})
// wait for all promises to complete
await Promise.all(promises)
console.log(list)
return res.json(list)
} catch (error) {
next(error)
}
}
In Sequelize (REST API on EXPRESS) only in POST (CREATE) request, i calling API Route which call function from controller where I use: creating instance with associations, model + association + route look fine, but i got error:
Error: Both replacements and bind cannot be set at the same time
I cant solve this, a spent a lot of time about this error, yet.
Please can anybody help me with this?
I tried:
middleware error handler with return async, same result.
Remove "freezeTableName", nothing changed.
config.js:
const AddressModel = require('../model/address.model');
const CompanyModel = require('../model/company.model');
const Address = AddressModel(sequelize, Sequelize);
const Company = CompanyModel(sequelize, Sequelize);
Company.belongsTo(Address);
....
router.js:
module.exports = function (app) {
let company = require('../controllers/company.controller.js');
// Create a new Customer
app.post('/api/company', company.createCompany);
....
company.controller.js:
const db = require('../config/db.config.js');
const Company = db.Company;
const Address = db.Address;
// Post a Company
exports.createCompany = async (req, res) => {
const transaction = await db.sequelize.transaction();
try {
let company = await Company.create({
name: req.body.name,
Address: {
whole_address: 'TEST ADDRESS FIELD',
ruian_adm_code: 126252
}
}, {
include: [Address],
transaction
});
await transaction.commit();
if (company) {
res.json({
success: 1,
data: company,
message: 'Company created.'
});
}
} catch (ex) {
await transaction.rollback();
res.json({success: 0, message: ex});
}
};
....
RETURN = Error: Both replacements and bind cannot be set at the same time
I am trying to send a post request from a node + express server to my Foxx service on Arangodb.
On the node side :
var route = arangopi + '/edge/' + col.name ;
var body = {data: data, from: fromId, to: toId} ;
console.log('|| body :', route, body) ;
>> || body : http//XXX/_db/my-DB/my-foxx-service/path/to/visitedBy { data: { isBackup: true, text: '', isHint: true, continuance: 3441.5 }, from: 'Drop/27237133', to: 'Bot/41116378' }
return requestify.post (route, body)
On the Foxx side, I receive the request but the logs tell me it has no body :
router.post('/path/to/:param', function (req, res) {
console.log ('|| body :', req.body)
var data = req.body ;
var result = api.DoSomething (req.stateParams.param, data)
res.send(result)
})
.response(joi.object().required(), 'Entry stored in the collection.')
.summary('Summary')
.description('Description')
>> || body : [Object { "binarySlice" : function binarySlice() { [native code] }, "asciiSlice" : function asciiSlice() { [native code] }, "base64Slice" : function base64Slice() { [native code] }, "ucs2Slice" : function ucs2Slice() { [native code] }, "hexSlice" : f...
On the node side I also tried the 'request' module.
return request.post(route, {form:body}, function (error, response, body) {
console.log('error:', error);
console.log('statusCode:', response && response.statusCode);
console.log('body:', body);
return response ;
});
And I get the same logs from Foxx.
What do I do wrong ?
Here is a screenshot of my operation on the Foxx interface. Is it normal that I cannot specify a request body for testing ?
I think the reason is because you haven't specified in the end point in Foxx that there is a body expected as part of the .post.
It took me a while to work out a way of defining Foxx MicroServices, and I read through a number of ArangoDB example code before I settled on a pattern.
To help you get started, I've provided how I would quickly mock up the Foxx MicroService code in a way that is extensible, allowing you to separate your Routes from your Models.
Use these as examples to get your example working.
I've made assumptions that there are two document collections, 'Drop' and 'Bot' with an edge collection that joins them called 'VisitedBy'.
All these files are stored on your Foxx MicroService:
main.js
'use strict';
module.context.use('/v1/visitedBy', require('./routes/visitedBy'), 'visitedBy');
routes/visitedBy.js
'use strict';
const request = require('#arangodb/request');
const joi = require('joi');
const createRouter = require('#arangodb/foxx/router');
const VisitedBy = require('../models/visitedBy');
const visitedDataSchema = joi.object().required().description('Data that tracks a visited event');
const router = createRouter();
module.exports = router;
/*********************************************
* saveVisitedBy
* Path Params:
* none
* Query Params:
* none
* Body Params:
* body (required) The data that is used to record when something is visited
*/
router.post('/', function (req, res) {
const visitedData = req.body;
const savedData = VisitedBy.saveVisitedByData(VisitedBy.fromClient(visitedData));
if (savedData) {
res.status(200).send(VisitedBy.forClient(savedData));
} else {
res.status(500).send('Data not saved, internal error');
}
}, 'saveVisitedBy')
.body(visitedDataSchema, 'visited data')
.response(VisitedBy.savedDataSchema, 'The response after the data is saved')
.summary('Save visited data')
.description('Save visited data');
models/visitedBy.js
'use strict';
const _ = require('lodash');
const joi = require('joi');
const db = require('#arangodb').db;
const visitedByEdgeCollection = 'VisitedBy';
/*
Schema for a response after saving visitedBy data
*/
const savedDataScema = {
id: joi.string(),
data: joi.object(),
_from: joi.string(),
_to: joi.string()
};
module.exports = {
savedDataSchema: savedDataScema,
forClient(obj) {
// Implement outgoing transformations here
// Remove keys on the base object that do not need to go through to the client
if (obj) {
obj = _.omit(obj, ['_id', '_rev', '_oldRev', '_key']);
}
return obj;
},
fromClient(obj) {
// Implement incoming transformations here
return obj;
},
saveVisitedByData(visitedData) {
const q = db._createStatement({
"query": `
INSERT {
_from: #from,
_to: #to,
data: #data,
date: DATE_NOW()
} IN ##col
RETURN MERGE ({ id: NEW._id }, NEW)
`
});
q.bind('#col', visitedByEdgeCollection);
q.bind('from', visitedData.from);
q.bind('to', visitedData.to);
q.bind('data', visitedData.data);
const res = q.execute().toArray();
return res[0];
}
};
Your service should look like this in the Swagger interface:
You can learn more about using joi to define data structures here.
It takes a bit getting used to joi, but once you get some good working examples you can define great data definitions for incoming and outgoing data.
I hope this helps, it was difficult for me getting a basic MicroService code model that made it clear how things operated, I'm sure a lot can be done for this example but it should be a good starting spot.
As David Thomas explained in his answer, I needed to specify a body format in my router code (Foxx side).
In short :
const bodySchema = joi.object().required().description('Data Format');
router.post('/path/to/:param', function (req, res) {
var data = req.body ;
var result = api.DoSomething (req.stateParams.param, data)
res.send(result)
})
.body(bodySchema, 'Body data')
.response(joi.object().required(), 'Entry stored in the collection.')
.summary('Summary')
.description('Description')