I'm testing my code with Jest and mocking http call with nock JSON response:
it('should throw error when record does not exists', async() => {
const noExistsJSONFile = path.join(__dirname, "../../../data/api/project/non-existing.json");
nock(__ROOT_API__)
.defaultReplyHeaders({ 'access-control-allow-origin': '*' })
.get('/projects/2500')
.replyWithFile(404, noExistsJSONFile);
const response = await getProject('/projects/2500');
expect(response).toThrowError(`${notExisting.errorMessage} (Code: ${notExisting.errorCode})`);
});
});
There is an async function which should fail if the status code is > 300 and create new Error:
export async function get(pathname) {
const params = {
headers: {
'Content-Type': 'application/json',
}
};
let response = await fetch(`${__ROOT_API__}${pathname}`, params);
if (!validateStatusCode(response)) {
var error = new Error(response.statusText)
error.response = response
throw error
}
return response.json();
}
At moment when I'm running test JEST is throwing:
11 | let response = await fetch(`${__ROOT_API__}${pathname}`, params);
12 | if (!validateStatusCode(response)) {
> 13 | var error = new Error(response.statusText)
14 | error.response = response
15 | throw error
16 | }
at _callee$ (src/services/project/project.js:13:19)
at tryCatch (node_modules/regenerator-runtime/runtime.js:62:40)
at Generator.invoke [as _invoke] (node_modules/regenerator-runtime/runtime.js:296:22)
at Generator.prototype.(anonymous function) [as next] (node_modules/regenerator-runtime/runtime.js:114:21)
at step (node_modules/babel-runtime/helpers/asyncToGenerator.js:17:30)
at node_modules/babel-runtime/helpers/asyncToGenerator.js:28:13
When I looking on chrome network traffic I response which I need. What is the reason behind JEST error
It's because you're starting the fetch, thus, throwing the error before the toThrowError matcher. Furthermore, this is an async call, which makes it a bit more complex to test, for this reason, instead of using the toThrow matcher which is used for sync functions calls, you can use a try/catch block with the await syntax, as explained in the documentation:
expect.assertions(1);
try {
await getProject('/projects/2500');
} catch (e) {
expect(e).toMatch(`${notExisting.errorMessage} (Code: ${notExisting.errorCode})`);
}
expect.assertions(1) tells jest to expect one assertion inside the test.
I done small changes to code - not working 100% as expected but close.
Change a little bit response function :
export async function get(pathname) {
const params = {
headers: {
'Content-Type': 'application/json',
}
};
let response = await fetch(`${__ROOT_API__}${pathname}`, params);
if (!validateStatusCode(response)) {
const res = await response.json();
throw new Error(res.errorMessage);
}
return response.json();
}
And test looks like this:
it('should throw error when record does not exists', async () => {
const noExistsJSONFile = path.join(__dirname, "../../../data/api/project/non-existing.json");
nock(__ROOT_API__)
.defaultReplyHeaders({ 'access-control-allow-origin': '*' })
.get('/projects/2500')
.replyWithFile(404, noExistsJSONFile);
try {
await getProject('/projects/2500');
} catch (error) {
expect(error).toEqual(new Error('error message'));
}
});
Related
I'm trying to dynamically generate routes in my next.js application. I have an api called getUsers that returns something like this:
{"users":[{"_id":"639a87ae8a128118cecae85b","username":"STCollier","image":"https://storage.googleapis.com/replit/images/1641322468533_db666b7453a6efdb886f0625aa9ea987.jpeg","admin":false,"likedPosts":["639e34c5991ecaea52ace9e4","639e34c7991ecaea52ace9e7","639e34c7991ecaea52ace9ea","639e39a216a642f686a28036","639e39a216a642f686a28037","639e3b3d8cdebd89d9691f97","639e3b3d8cdebd89d9691f98","639e3b3e8cdebd89d9691f9d","639e3b5a8cdebd89d9691fa0","639e3b5c8cdebd89d9691fa3","639e3b5c8cdebd89d9691fa6"],"dislikedPosts":[""]},{"_id":"639a88abc4274fba4e775cbe","username":"IcemasterEric","image":"https://storage.googleapis.com/replit/images/1646785533195_169db2a072ad275cfd18a9c2a9cd78a1.jpeg","admin":false,"likedPosts":[],"dislikedPosts":[]}
So I know the API works succesfully, but when trying to get these api results and generate a page for each username, I get an error stating:
TypeError: users.map is not a function
Here's my code for generating the routes:
//pages/user/[username].js
const Get = async (url) => {
return await fetch(url).then(r => r.json());
}
export async function getStaticPaths() {
const users = Get('/api/getUsers')
return {
paths: users.map(u => {
const username = u.users.username
return {
params: {
username
}
}
}),
fallback: false
}
}
What is wrong with my getStaticPaths() code? I know that the API is working, so why can't I map the results?
And if anyone needs the code for api/getUsers, here is that:
import clientPromise from "../../lib/mongodb";
import nc from "next-connect";
const app = nc()
app.get(async function getUsers(req, res) {
const client = await clientPromise;
const db = client.db("the-quotes-place");
let users = []
try {
const dbUsers = await db
.collection("users")
.find({})
.toArray();
users = dbUsers
return res.json({
users: JSON.parse(JSON.stringify(users)),
success: true
})
} catch(e) {
return res.json({
message: new Error(e).message,
success: false,
});
}
})
export default app
Thanks for any help!!
Modify Get method to return an async value instead of Promise.
As Get is an async method, you need the await in getStaticPaths method.
const Get = async (url) => {
let response = await fetch(url);
return await response.json();
}
export async function getStaticPaths() {
const users = await Get('/api/getUsers');
...
}
I get this error whenever I call the netlify function. sometimes it works and sometimes not and I get this back
Request from ::ffff:127.0.0.1: GET /.netlify/functions/getS3URL?reqType=get
Response with status 200 in 2919 ms.
C:.…AppData\Roaming\npm\node_modules\netlify-cli\node_modules\netlify-redirector\lib\redirects.js:1…
TypeError: res.writeHead is not a function
at ProxyServer. (AppData\Roaming\npm\node_modules\netlify-cli\src\utils\proxy.js:318:9)
at ProxyServer.emit (Roaming\npm\node_modules\netlify-cli\node_modules\eventemitter3\index.js:204:33)
at Socket.onOutgoingError (AppData\Roaming\npm\node_modules\netlify-cli\node_modules\http-proxy\lib\http-proxy\passes\ws-incoming.js:157:16)
at Socket.emit (node:events:525:35)
at Socket.emit (node:domain:489:12)
at emitErrorNT (node:internal/streams/destroy:157:8)
at emitErrorCloseNT (node:internal/streams/destroy:122:3)
at processTicksAndRejections (node:internal/process/task_queues:83:21)
api.service:
getS3URL(){
let queryParams = new HttpParams();
return this.http.get(${baseUrl}getS3URL,{ params: queryParams }).pipe();
}
In the component:
getS3URL() {
if (this.product.images[0]) {
this.api.getS3URL().subscribe({
next: (value: any) => { console.log('resp: ',value)}
, error: (err) => { console.log(‘error’, err) }
})
}
}
Netlify function:
import dotenv from ‘dotenv’
import aws from ‘aws-sdk’
import crypto from ‘crypto’
import { promisify } from “util”
import { Response } from ‘#netlify/functions/dist/function/response’
dotenv.config()
const randomBytes = promisify(crypto.randomBytes)
const region = “us-east-1”
const bucketName = “-----”
const accessKeyId = process.env[‘AWS_ACCESS_KEY_ID’]
const secretAccessKey = process.env[‘AWS_SECRET_ACCESS_KEY’]
const s3 = new aws.S3({
region,
accessKeyId,
secretAccessKey,
signatureVersion: ‘v4’
})
exports.handler = async (event: any, context: any, callback: any) => {
let resp: Response
let putURL: string = ‘’
try {
const rawBytes = await randomBytes(16)
const imageName = rawBytes.toString()
var params = { Bucket: bucketName, Key: imageName, Expires: 60 };
var promise = await s3.getSignedUrlPromise(‘putObject’, params).then(value=>putURL=value)
resp = {
statusCode: 200,
body: JSON.stringify({
URL:putURL
})
}
} catch (err: any) {
console.log(err.stack)
resp = {
statusCode: 400,
body: err.stack
};
}
return resp
}
I’m using in my project other netlify functions to do some other api requests and they are working just fine.
Used versions:
angular 14
netlify: 12.0.1
netlify-cli: 12.0.11
Thanks
I'm currently using axios and NextJS.
I currently have this code in my component:
export async function getServerSideProps(context) {
const data = await getVideo(context.query.id);
console.log('data: ', data);
// console.log('context: ', context);
console.log('context params: ', context.params);
console.log('context query: ', context.query);
if (!data) {
return { notFound: true };
}
return {
props: {
videoId: context.params.id,
videoSlug: context.params.slug,
videoContent: data
}
};
}
This getserverSideProps call the function of getVideo which looks exactly like this:
export const getVideo = (id) => async (dispatch) => {
dispatch({ type: CLEAR_VIDEO });
try {
console.log('Action file: ', id);
const res = await api.get(`/videos/${id}`);
return dispatch({
type: GET_VIDEO,
payload: res.data
});
} catch (err) {
dispatch({
type: VIDEO_ERROR,
payload: { msg: err.response?.statusText, status: err.response?.status }
});
}
};
Said function goes through my api function to make requests to backend:
import axios from 'axios';
import { LOGOUT } from '../actions/types';
import { API_URL } from '../config';
const api = axios.create({
baseURL: `${API_URL}/api/v1`,
headers: {
'Content-Type': `application/json`
}
});
/**
intercept any error responses from the api
and check if the token is no longer valid.
ie. Token has expired
logout the user if the token has expired
**/
api.interceptors.response.use(
(res) => {
res;
console.log('Res: ', res.data);
},
(err) => {
if (err?.response?.status === 401) {
typeof window !== 'undefined' &&
window.__NEXT_REDUX_WRAPPER_STORE__.dispatch({ type: LOGOUT });
}
return Promise.reject(err);
}
);
export default api;
It works great when doing POST, PUT,PATCH requests.
As you can see, I'm doing a console.log('data: ',data) but it returns [AsyncFunction (anonymous)] whenever I read the terminal; on the other hand, the front-end returns this error:
Server Error Error: Error serializing .videoContent returned from
getServerSideProps in "/videos/[id]/[slug]". Reason: function
cannot be serialized as JSON. Please only return JSON serializable
data types.
Does anyone knows how to solve this?
NOTE: I'm using react-redux, redux and next-redux-wrapper.
That is because your getVideo function returns another function. The right way to call it would be:
const data = await getVideo(context.query.id)()//<- pass in the dispatch here
But you should not use redux in the backend like that. I think you can completely remove it.
export const getVideo async (id) => {
try {
console.log('Action file: ', id);
const res = await api.get(`/videos/${id}`);
return res.data
});
} catch (err) {
return { msg: err.response?.statusText, status: err.response?.status }
}
};
// call
const data = await getVideo(context.query.id)
This question already has answers here:
How can I get the status code from an HTTP error in Axios?
(15 answers)
Closed 7 days ago.
I'm using blob responseType with Axios in my VueJS app for downloading a document from the server. When the response code is 200 it works fine and download the file but when there is any http error, I'm not able to read the status code when I catch the error because the error is a JSON response.
Has anyone had a similar issue and worked out a way to convert the blob response type to json and thrown an error based on the status code?
I have tried sending the response as a plain text from Laravel backend and tried converting the response to JSON or text in the front-end but no luck.
I have tried reading error response headers but no luck.
Axios({
url: 'xxxx',
method: 'GET',
responseType: 'blob',
})
.then((response) => {
//code to read the response and create object url with the blob and download the document
})
.catch((error) => {
console.log('Error', error.message); //nothing
console.log('Error', error.error); //undefined
console.log('Error', error.data); //undefined
const blb = new Blob([error], {type: "text/plain"});
const reader = new FileReader();
// This fires after the blob has been read/loaded.
reader.addEventListener('loadend', (e) => {
const text = e.srcElement.result;
console.log(text);
});
// Start reading the blob as text.
reader.readAsText(blb);
});
I just want to throw the error message based on the status code. If it's 401 just want it to be unauthorized and anything else throw it on to the component.
The reason is that the response type is blob.
In case of error, the status code is available directly in your exception object. However, the response is a promise.
What you need to do is:
.catch((error) => {
let statusCode = error.response.status
let responseObj = await error.response.data.text();
:
:
For more details you can read documentation.
You can do this, it cast the error in JSON if it's a blob or let the response data as it received and you can do work with it.
let errorString = error.response.data;
if (
error.request.responseType === 'blob' &&
error.response.data instanceof Blob &&
error.response.data.type &&
error.response.data.type.toLowerCase().indexOf('json') != -1
) {
errorString = JSON.parse(await error.response.data.text());
}
alert(errorString);
You need to convert the response blob to json:
Axios({
url: 'xxxx',
method: 'GET',
responseType: 'blob',
})
.then((response) => {
//code to read the response and create object url with the blob and download the document
})
.catch((error) => {
if (
error.request.responseType === 'blob' &&
error.response.data instanceof Blob &&
error.response.data.type &&
error.response.data.type.toLowerCase().indexOf('json') != -1
) {
new Promise((resolve, reject) => {
let reader = new FileReader();
reader.onload = () => {
error.response.data = JSON.parse(reader.result);
resolve(Promise.reject(error));
};
reader.onerror = () => {
reject(error);
};
reader.readAsText(error.response.data);
})
.then(err => {
// here your response comes
console.log(err.response.data)
})
};
});
You can use this inside interceptor.
For more info
I believe you might be using the error variable in your catch() incorrectly.
Axios passes an error object that has a response property that is where you will want to look for your error or message.
https://github.com/axios/axios#handling-errors
On a side note if you can catch the error server side you could try setting the Content-type header to text/plain. Using either header('Content-Type: plain/text') or Laravel's Response Methods
you can do the following way
axios.post("URL", data, {
validateStatus: (s) => s <= 500,
responseType: 'blob',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/pdf'
}
})
.then(async (response) => {
if (response.status !== 200) {
// error handling
const error = JSON.parse(await response.data.text());
console.log('error: ', error);
alert(error.message);
} else {
console.log('res', response)
const url = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', 'file.pdf'); //or any other extension
document.body.appendChild(link);
link.click();
}
})
You can convert blob response globaly:
$axios.onError(async ({request, response}) => {
const status = response.status
let data = response.data
if (request.responseType === 'blob' && data instanceof Blob && data.type) {
const text = await data.text()
data = data.type.toLowerCase().includes('json') ? JSON.parse(text) : text
}
throw {status, data} // error model
})
catch(async(error) => {
const errorJson = JSON.parse(await error.response.data.text());
console.log('error: ', errorJson.error_message);
})
I have a JSON read to UI and modifiled as part of review process. Finally I want to save New data to a New folder/File.
Code Sample below - Always ends with POST call in error :
SaveFinalData(){
this.postJson<Reaction>
('./assets/ICP/Reviewed/Json/out.json',
this.reactionDatabase.reactiondataChange.value);
}
postJson<Reaction>(url: string, Reaction: any){
//let body = JSON.stringify({ Reaction });
this.http.post(url, body)
.subscribe(
(val) => {
console.log("POST call successful value returned in body", val);
},
response => {
console.log("POST call in error", response);
},
() => {
console.log("The POST observable is now completed.");
});
}
Have tried below 2 alternatives:
(1)
var theJSONdata = JSON.stringify({ Reaction });
window.localStorage.setItem('./assets/ICP/Reviewed/Json/out.json', theJSONdata)
Result -- NO LUCK!
(2)
var jsonfile = require('jsonfile')
var file = './assets/ICP/Reviewed/Json/out.json'
var obj = {name: 'JP'} //SAMPLE DATA
jsonfile.writeFile(file, obj, function (err) {
console.error(err)
})
Result -- NO LUCK! --> GIVES ERROR fs.writeFile is not a function
Pls kindly help/guide me to reach the final result....Thanks