The component renders the error state just fine, but the exception is displayed as uncaught in the console and a dialogue is displayed in next dev on the browser. Is there a way to handle expected errors to squelch this behavior?
import { useMutation, gql } from "#apollo/client";
import { useEffect } from "react";
const CONSUME_MAGIC_LINK = gql`
mutation ConsumeMagicLink($token: String!) {
consumeMagicLink(token: $token) {
token
member {
id
}
}
}
`;
export default function ConsumeMagicLink({ token }) {
const [consumeMagicLink, { data, loading, error }] =
useMutation(CONSUME_MAGIC_LINK);
console.log("DATA", data, "loading:", loading, "error:", error);
useEffect(() => {
try {
consumeMagicLink({ variables: { token } });
} catch (e) {
console.log(e);
}
}, []);
var text = "Link has expired or has been used previously";
if (data) text = "SUCCESS: REDIRECTING";
if (loading) text = "Processing";
if (error) text = "Link has expired or has been used previously";
return (
<div>
<h2>{text}</h2>
</div>
);
}
Console Results:
Error Displayed in Browser:
The error is from the client instead of the mutation so your try-catch cannot catch it. To handle this you can add the error handling to the client, for example:
const errorLink = onError(({ graphQLErrors, networkError }) => {
if (graphQLErrors)
graphQLErrors.forEach(({ message, locations, path }) =>
console.log(
`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`,
),
);
if (networkError) console.log(`[Network error]: ${networkError}`);
});
const httpLink = new HttpLink({
uri: "some invalid link"
});
const client = new ApolloClient({
link:from([httpLink,errorLink]),
cache: new InMemoryCache()
})
And as you got authorization error, I suggest you check your headers.
You can find additional information and examples of this approach here:
enter link description here
Related
I need your help to mock a twilio service which sends a message, using jest to mock the service
I have the next code:
import { SQSEvent } from "aws-lambda";
import { GetSecretValueResponse } from "aws-sdk/clients/secretsmanager";
export async function sendSms(event: SQSEvent, data: GetSecretValueResponse) {
const secrets = JSON.parse(data.SecretString);
const accountSid = secrets.TWILIO_ACCOUNT_SID;
const authToken = secrets.TWILIO_AUTH_TOKEN;
const twilioNumber = secrets.TWILIO_PHONE_NUMBER;
if (accountSid && authToken && twilioNumber) {
//Create a Twilio Client
const client = new Twilio(accountSid, authToken);
//Loop into al records of the event, every record is every message sent from Sqs
for (const record of event.Records) {
const body = JSON.parse(record.body);
const userNumber = "+" + body.number;
//SendMessage function
try {
const message = client.messages.create({
from: twilioNumber,
to: userNumber,
body: body.message,
});
return message;
} catch (error) {
return `Failed to send sms message. Error Code: ${error.errorCode} / Error Message: ${error.errorMessage}`;
}
}
} else {
return "You are missing one of the variables you need to send a message";
}
}
The I call this function from my index:
import { SQSEvent } from "aws-lambda";
import { sendSms } from "./services/sendSms/sendSms";
import { getSecret } from "./services/obtainSecrets/getSecret";
import { SecretsManager } from "aws-sdk";
export const lambdaHandler = async (event: SQSEvent) => {
try {
const obtainedSecret = await getSecret()
.then((credentials: SecretsManager.GetSecretValueResponse) => {
return credentials;
})
.catch(error => {
return error;
});
const response = sendSms(event, obtainedSecret)
.then(response => {
return response;
})
.catch(error => {
return error;
});
return {
message: "OK " + obtainedSecret + response,
code: 200,
};
} catch (error) {
throw new Error(error);
}
};
I have already make some tests, but them always makes a connection with Twilio api(requiring the real token, sid,etc), and I need to mock the Twilio service, so the function I call in my test.ts doesn't connects to internet.
import { Twilio } from "twilio";
import { MessageInstance } from "twilio/lib/rest/api/v2010/account/message";
import { sendSms } from "../../services/sendSms/sendSms";
//mock Twilio library and sendSms service
jest.mock("twilio");
jest.mock("../../services/sendSms/sendSms");
const smsMessageResultMock: Partial<MessageInstance> = {
status: "sent",
sid: "AC-lorem-ipsum",
errorCode: undefined,
errorMessage: undefined,
};
describe("SMS Service", () => {
describe("Send Message", () => {
it("Should fail", async () => {
// update smsMessageResultMock to simulate a faled response
const smsMessageMock = {
...smsMessageResultMock,
status: "failed",
errorCode: 123,
errorMessage: "lorem-ipsum",
};
// simulated response of secret management
let data = {
ARN: "arn:aws:secretsmanager:us-west-2:123456789012:secret:MyTestDatabaseSecret-a1b2c3",
Name: "MyTestDatabaseSecret",
SecretString:
'{"TWILIO_ACCOUNT_SID": "ACTWILIO_ACCOUNT_SID","TWILIO_AUTH_TOKEN": "TWILIO_AUTH_TOKEN","TWILIO_PHONE_NUMBER": "TWILIO_PHONE_NUMBER"}',
VersionId: "EXAMPLE1-90ab-cdef-fedc-ba987SECRET1",
VersionStages: ["AWSPREVIOUS"],
};
// simulated response of SqsEvent
let event = {
Records: [
{
messageId: "19dd0b57-b21e-4ac1-bd88-01bbb068cb78",
receiptHandle: "MessageReceiptHandle",
body: '{"message": "Hello world","number": "(506)88888888"}',
attributes: {
ApproximateReceiveCount: "1",
SentTimestamp: "1523232000000",
SenderId: "123456789012",
ApproximateFirstReceiveTimestamp: "1523232000001",
},
messageAttributes: {},
md5OfBody: "{{{md5_of_body}}}",
eventSource: "aws:sqs",
eventSourceARN: "arn:aws:sqs:us-east-1:123456789012:MyQueue",
awsRegion: "us-east-1",
},
],
};
// simulate tokens for Twilio
const accountSid = "ACfjhdskjfhdsiuy876hfijhfiudsh";
const authToken = "fjfuewfiuewfbodfiudfgifasdsad";
//create client with mocked Twilio
const client = new Twilio(accountSid, authToken);
//call messages.create of Twilio client, and give it the expected result created
client.messages.create = jest
.fn()
.mockResolvedValue({ ...smsMessageMock });
console.log(await sendSms(event, data));
//expectes the function sendSms(event, data) to throw an error
await expect(sendSms(event, data)).rejects.toThrowError(
`Failed to send sms message. Error Code: ${smsMessageMock.errorCode} / Error Message: ${smsMessageMock.errorMessage}`
);
});
});
});
(event and data are simulated responses of SqsEvent and GetSecretValueResponse)
The problem is that when I run the npm test it throws me an error of Twilio's authentication, an it is because I'm passing self created tokens.
Expected substring: "Failed to send sms message. Error Code: 123 / Error Message: lorem-ipsum"
Received message: "Authentication Error - invalid username"
at success (node_modules/twilio/lib/base/Version.js:135:15)
at Promise_then_fulfilled (node_modules/q/q.js:766:44)
at Promise_done_fulfilled (node_modules/q/q.js:835:31)
at Fulfilled_dispatch [as dispatch] (node_modules/q/q.js:1229:9)
at Pending_become_eachMessage_task (node_modules/q/q.js:1369:30)
at RawTask.Object.<anonymous>.RawTask.call (node_modules/asap/asap.js:40:19)
at flush (node_modules/asap/raw.js:50:29)
So what I suppose is that the test is connecting to internet and calling Twilio's api.
I appreciate if you could help me.
I think what you want to do is mock the class returned by the module, using jest.mock('twilio', mockImplementation) and in mockImplementation return a function to act as a constructor that will take your account SID and auth token arguments and then return a mockClient implementation, which in this case needs to return an object which has a messages property, which in turn is an object with a create property that is a mock function.
It's probably easier to just show the code.
const mockClient = {
messages: {
create: jest.fn().mockResolvedValue({ ...smsMessageMock });
}
};
jest.mock("twilio", () => {
return function(accountSid, authToken) {
return mockClient;
}
});
How can I set the 404 status code for the custom 404-page in Next.js? I am a frontend, getting familiar with backend, but according to this thread https://github.com/vercel/next.js/discussions/10960
it should be possible to do in getStaticProps, but the thing is, perhaps I understand it incorrectly, but it allows doing stuff like returning notFound or redirect from getStaticProps only if the data that is fetched inside this function is e.g. empty so in this example
export async function getStaticProps({ params: { id } }) {
const res = await fetchContentfulPosts();
const posts = await res
.filter((r) => r.sys.id === id)
.map((p) => {
return {
fields: p.fields,
locale: p.sys.locale,
tags: p.metadata.tags,
};
});
const { locale, fields, tags } = posts[0];
const { title, author, date, image, text, shareDesc } = fields;
if (!posts) {
return {
redirect: {
destination: '/404',
statusCode: 301,
},
};
}
return {
props: {
title,
author,
date,
text,
image: image || null,
locale,
tags,
shareDesc
},
};
}
It would return 404 status only if the data returned from Contentful were empty, but what about if someone enters url that does not exist? Then I will get the 404 with the status code 200.
I also tried to modify the error page according to this doc https://nextjs.org/docs/advanced-features/custom-error-page like so:
import { Layout, NotFoundComponent } from '../components';
function Error({ statusCode }) {
return statusCode === 404 ? (
<Layout title="404: This page could not be found" backgroundVideo noIndex>
<NotFoundComponent />
</Layout>
) : (
<p>
{statusCode
? `An error ${statusCode} occurred on server`
: 'An error occurred on client'}
</p>
);
}
Error.getInitialProps = ({ res, err }) => {
const statusCode = res ? res.statusCode : err ? err.statusCode : 404;
return { statusCode };
};
export default Error;
But it seems that it only gets status code from the server and allows to display it, but not to set it to the server. I want all 404 hits to be returned with response code 404 so this would not be indexed.
Thanks very much!
I'm fetching a query to a GraphQL server that throws an error if something goes wrong. Now, I want to have access to that error but instead Apollo Client is showing me the general Error: Network error: Response not successful: Received status code 500.
By adding the onError property to my query, I can see the Error object in the console but only if it's wrapped in {}, otherwise it's just text.
This is my code:
const { data, loadMore, loading, error, retry } = useRetryableQuery<
GetDevices,
DevicesQueryVariables
>(devicesQueries.GetDevices, {
onError: (networkError) => {
console.log(networkError); //this will show me the error as a whole text and not an object
console.log({ networkError }) // this will show me an object which its properties I cannot access
},
errorPolicy: "all",
notifyOnNetworkStatusChange: true,
paginationPath: ["paging"],
itemsPaths: [["filteredDevices", "rows"]],
variables: {
...queryVariables,
connectionPaging: {
offset: 0,
limit: PAGE_SIZE,
},
},
});
How can I access the properties nested inside the networkError object?
First screenshot is without {}
Second one is with {}
Thanks in advance!
The issue was related to this Apollo Client issue: useQuery() refetch throws on error instead of flowing through hook, so getting the error from the ApolloProvider directly allowed me to access the error sent from the backend:
const errorLink = useMemo(
() =>
onError((error) => {
const message = getApolloError({ graphQLErrors: error.graphQLErrors
});
if (message) {
localStorage.setItem("error", message);
} else {
localStorage.removeItem("error");
}
if (message === "Session expired.") {
logout(true);
}
}),
[logout]
);
const client = useMemo(
() =>
new ApolloClient({
cache,
link: ApolloLink.from([errorLink, authLink, httpLink]),
defaultOptions: {
watchQuery: {
fetchPolicy: "network-only",
},
query: {
fetchPolicy: "network-only",
},
},
}),
[httpLink, errorLink]
);
I am trying to launch a post Axios request from my front to my back using React framework. When I use my localhost server its works but I use Heroku address, I have an error message (400 bad request) in my console.
I tried to launch other get Axios requests with heroku and it works. So I am wondering that the problem I have is related to Post Axios requests.
I will appreciate your comments
please find below my code (front react):
import React from "react";
import axios from "axios";
const Questions = () => {
const fetchData = async () => {
const response = await axios.post(
"https://formnest-back-certification.herokuapp.com/form/create",
{
title: "nouveau formulaire",
}
);
console.log(response.data);
};
fetchData();
return (
<>
<div>Hello form</div>
</>
);
};
export default Questions;
Here is my code in the back React:
router.post("/form/create", async (req, res) => {
try {
/* const titleForm = await Form.findOne({ title: req.fields.title });
console.log(titleForm);
if (titleForm) {
return res.status(400).json({ error: "title already used" });
} else { */
if (req.fields.title) {
const newForm = new Form({
title: req.fields.title,
/* questions: req.fields.questions,
answers: req.fields.answers, */
});
// Sauvegarde du formulaire
await newForm.save();
return res.json(newForm);
} else {
return res.status(400).json({ error: "Missing parameters" });
}
} catch (e) {
return res.status(400).json({ error: e.message });
}
/* console.log(req.fields); */
/*
res.json({ message: "form created" }); */
});
If you are using express framework for the POST request, you need to using body-parser module then using req.body to retrieve the request data.
req.fields is when you are using express-formidable module.
having 2 api's. method POST-Login method GET-data. and server has cors enabled. Login api working fine, but when call api with GET method it gets failed.
Code:
->api Login-POST
const login = async (email, password) => {
console.log("in auth service");
const userDetail = {
username:email,
// email,
password
};
try {
// unsetHeadersWithUserToken();
const afterSuccess = await api.post(apiDetail.auth.url, userDetail);
if (afterSuccess) {
return afterSuccess.data;
}
} catch (error) {
console.log("error: ", error.response.error);
if (error.category === 'User Permissions') {
// forceLogout();
}
throw error;
}
};
->api-GET
try{
// console.log("url : ", apiDetail.partnerLocations.url);
let token = sessionStorage.getItem('token');
setHeadersWithUserToken(token);
let apiResponse = await api.get(apiDetail.partnerLocations.url);
return apiResponse;
}catch(error){
console.info('##### demand-response.js:11 #####');
console.info('========================= Start =========================');
console.error('error = ', JSON.stringify(error));
// console.log(error.response.data)
console.info('========================== End ==========================');
throw error;
}
->axios call
import axios from 'axios';
import { environment } from '../../utils/constants';
let api;
let apiDetail = {
baseURL: environment.baseURL,
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*'
}
};
const setAPI = apiDetail => {
api = axios.create(apiDetail);
};
setAPI(apiDetail);
const setHeadersWithUserToken = token => {
api.defaults.headers.common['Authorization'] = token;
};
export {
api,
setHeadersWithUserToken,
};
Image-1
showing console error
Image-2
network call response
Try this
const proxyurl = "https://cors-anywhere.herokuapp.com/"
cosnt url = 'Your URL'
axios.get(proxyurl + url)
I faced the same issue and this works nicely.
Add the "proxy" property (found at the bottom here) to package.json:
"proxy": "http://localhost:<PORT-GOES-HERE>"
Now, instead of making HTTP requests like this:
axios.get("http://localhost:8080/example")
You should write them like this:
axios.get("/example")