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")
Related
Im making a user authorization process with JWT tokens.
How does the flow look like?
User logs in - gets an access token and a refresh token from a server, as a response
Access token comes in json body and is saved in local storage. Refresh token comes in a httpOnly cookie.
User can use getAllUsers method untill access token is valid.
Whenever getAllUsers method returns 401 unauthorized (when access token expires), there is a request being sent to refresh token endpoint - getRefreshToken, which returns new access token that is being saved to local storage
Refresh token expires and user is being logged out.
Whole flow in Postman works but i have got problem at frontend side.
Function getAllUsers works until access token expires.
Thats why I made a global function in a util file that checks if a response is 401 and if so, it sends a request to get a new access token and calls a function which returned that error.
However it does not work.
I think that the problem is in getAllUsers function which immediately goes to catch block (when cant fetch list of users because of 401) and does not invoke that global function from util file. Console logs from both functions (getDataFromResponse, getRefreshToken) does not work so it does not even get there.
Any ideas??
API utils file
import { AxiosResponse } from "axios";
import { apiService } from "./api.service";
type ApiServiceMethods = keyof typeof apiService;
export const getDataFromResponse = async (
response: AxiosResponse,
funName: ApiServiceMethods,
...args: any
): Promise<any> => {
if (response.status === 401) {
console.log("error");
await apiService.getRefreshToken();
return await apiService[funName](args);
}
return response.data;
};
API Service:
import { getDataFromResponse } from "./api.utils";
import axios from "./axios";
type LoginArgs = {
password: string;
username: string;
};
const apiServiceDef = () => {
const login = async (args: LoginArgs) => {
try {
const response = await axios.post("/login", {
username: args.username,
password: args.password,
});
const { data } = response;
const { token } = data;
localStorage.setItem("accessToken", token);
return response;
} catch (e) {
throw new Error("Custom");
}
};
/* problem here */
const getAllUsers = async () => {
const Token = localStorage.getItem("accessToken");
try {
const response = await axios.get("/users", {
headers: {
Token,
},
});
return await getDataFromResponse(response, "getAllUsers");
} catch (e) {
console.log(e);
}
};
/* problem here */
const getRefreshToken = async () => {
try {
console.log("fetch new access token");
const response = await axios.get("/refreshToken");
if (response.status === 401) {
localStorage.removeItem("accessToken");
throw new Error("TokenExpiredError");
}
const { data } = response;
const { token } = data
localStorage.setItem("accessToken", token);
return response;
} catch (e) {
console.log(e);
}
};
return { login, getRefreshToken, getAllUsers };
};
export const apiService = apiServiceDef();
I usually use a wrapper around the async functions or just use axios interceptors (https://stackoverflow.com/a/47216863/11787903). Be sure that err.response.status is right property, not sure about that, but this solution should work for you.
const asyncWrapper = async (handler) => {
try {
return handler()
} catch (err) {
if (err.response.status === 401) {
// refresh token then again call handler
await refreshToken()
return handler()
}
}
}
const getAllUsers = asyncWrapper(() => {
const Token = localStorage.getItem("accessToken");
return axios.get("/users", {
headers: {
Token,
},
});
});
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;
}
});
I have never had this happen before and am not sure why it's happening.
I have a component written to display PDF files in an iframe as part of a larger application. I am retrieving a BLOB stream from the server and attempting to create a URL for it to display in the iframe but it keeps giving me a cross-origin error, which I thought would not be possible since it is creating the URL out of data.
Here is my entire component:
import React, { useState, useEffect } from 'react'
import IFrameComponent from '../Elements/IFrameComponent';
const PDFPages = (props) => {
let [file, setFile] = useState(null)
let [notFound, show404]=useState(false)
useEffect(() => {
let id=props.site?.componentFile;
fetch(`${process.env.REACT_APP_HOST}/documents/GetPDF`,
{
method: 'POST'
, headers: {
'Content-Type': 'application/json'
}
, credentials: 'include'
, body: JSON.stringify({file:id})
})
.then(async response => {
let blob;
try{
blob=await response.blob(); // <--- this functions correctly
}
catch(ex){
let b64=await response.json()
blob=Buffer.from(b64.fileData,'base64')
}
//Create a Blob from the PDF Stream
//Build a URL from the file
const str=`data:application/pdf;base64,${b64.fileData}`
const url=URL.createObjectURL(blob) //<--- ERROR IS THROWN HERE
setFile(url);
})
.catch(error => {
show404(true)
});
}, []);
if(!notFound){
return <IFrameComponent src={file} title=''>
Please enable iFrames in your browser for this page to function correctly
</IFrameComponent>
}
else {
return (
<>
<h3> File {file} could not be found on server</h3>
</>
)
}
}
export default PDFPages;
For completeness here is the GetPDF function from the server which is sending the file.
router.post('/GetPDF', async (req, res, next) => {
const props = req.body;
let fileName = props.file;
try {
fileName = fileName.replace(/%20/g, " ");
let options = {};
if (props.base64) options.encoding = 'base64'
let data = await dataQuery.loadFile(`./data/documentation/${fileName}`, options);
if (!props.base64) {
res.attachment = "filename=" + fileName
res.contentType = 'application/pdf'
res.send(data);
}
else{
res.send({fileData:data, fileName: fileName});
}
}
catch (ex) {
res.send({ error: true })
}
});
I have done very little work in node sending files but am positive my client code is good. Where am I going wrong here?
The problem was that I was trying to be too fancy sending a BLOB or Base64 data. After investigation I rewrote
router.post('/GetPDF', async (req, res, next) => {
const props = req.body;
let fileName = props.file;
try {
fileName = fileName.replace(/%20/g, " ");
let options = {};
if (props.base64) options.encoding = 'base64'
let data = await dataQuery.loadFile(`./data/documentation/${fileName}`, options);
if (!props.base64) {
res.attachment = "filename=" + fileName
res.contentType = 'application/pdf'
res.send(data);
}
else{
res.send({fileData:data, fileName: fileName});
}
}
catch (ex) {
res.send({ error: true })
}
});
on the server to
router.get('/GetPDF/:fileName', async (req, res, next) => {
let fileName = req.params.fileName
fileName = `./data/documentation/${fileName.replace(/%20/g, " ")}`;
try {
let data = await dataQuery.loadFile(fileName);
res.contentType("application/pdf");
res.send(data);
}
catch (ex) {
res.send({ error: true })
}
});
Then calling it from the client using
const url = `${process.env.REACT_APP_HOST}/documents/GetPDF/${props.site.componentFile}`
as the iFrame src sends the PDF properly as expected.
This same method also solved another problem with HTML pages sent from the server not functioning correctly.
I am relatively new in React but I am trying to create a class/method for network call. Nothing complex just a way to make the code readable.
I have a class:
class Auth {
getToken(username, password) {
const endpointOptions = {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username: `${username}`, password: `${password}` })
};
fetch(`${Constant.BASE_WP_URL}${Constant.TOKEN_ENDPOINT}`, endpointOptions)
.then(async response => {
const data = await response.json();
if (!response.ok) {
// get error message from body or default to response status
const error = (data && data.message) || response.status;
throw error;
}
return data;
})
.catch(error => {
throw error;
});
}
}
export default Auth;
I am trying to call it using :
import Auth from '../../data/network/Auth';
requestSignIn = (event) => {
event.preventDefault();
this.setState({loading: true})
try {
const authData = Auth.getToken(`${this.state.email}`, `${this.state.password}`);
sessionStorage.setItem('authToken', authData.token)
} catch (error) {
console.log("Connection to WP - Auth Token failed ")
console.error(error);
}
}
but React is complaining because getToken is not a function. I am trying to create a class Auth to have inside all methods/functions I need related to Auth process.
Also, is it the right way to handle the result ? is the try/catch as done works or should I do it differently as the getToken is an API call.
Any idea ?
pretty sure, it's easy but I can't find any interesting topics on Google.
Thanks
I think, if you want to use function directly in OOP of JavaScript, you must put static keyword in front of the function name.
In your auth file
static class Auth {
static getToken(username, password) {
...
}
}
In your index file
import Auth from '../../data/network/Auth';
const authData = Auth.getToken(`${this.state.email}`, `${this.state.password}`);
If you don't have static in front of the function name. You have to create a new instance of the class Auth in order to use the function inside.
import Auth from '../../data/network/Auth';
const AuthInit = Auth();
authData = AuthInit.getToken(`${this.state.email}`, `${this.state.password}`);
===========================
Update for applying asynchronous method
// ====== auth file
static class Auth {
static async getToken(username, password) {
...
// assign fetched data to data_fetch
const data_fetch = fetch(`${Constant.BASE_WP_URL}${Constant.TOKEN_ENDPOINT}`, endpointOptions)
.then(async response => {
const data = await response.json();
if (!response.ok) {
// get error message from body or default to response status
const error = (data && data.message) || response.status;
throw error;
}
return data;
})
.catch(error => {
throw error;
});
return data_fetch;
}
}
// ======= index file
import Auth from '../../data/network/Auth';
...
requestSignIn = async (event) => { // put async in front of your function
// the function outside (requestSignIn) must be async type
// in order to use await keyword for getToken() function
event.preventDefault();
this.setState({loading: true})
try {
// because your getToken function is now a async function, you can
// use "await" keyword in front of it to wait for fetching data to finish
const authData = await Auth.getToken(`${this.state.email}`, `${this.state.password}`);
sessionStorage.setItem('authToken', authData.token)
} catch (error) {
console.log("Connection to WP - Auth Token failed ")
console.error(error);
}
}
Hope this would help
but React is complaining because getToken is not a function
You've defined getToken as a method of an Auth instance, not a static function.
But you don't need an Auth class here at all, just use the proper exports/imports.
replace the Auth-class with:
export function getToken(username, password) {
//...
};
and you can either
/// import all exports from that file under the name `Auth`
import * as Auth from '../../data/network/Auth';
// ...
const authData = Auth.getToken(...);
or
// import these specific exports from that file.
import { getToken } from '../../data/network/Auth';
// ...
const authData = getToken(...);
The last option has the advantage that it can be tree-shaken. If You have some build-process, the compiler can eliminate all the pieces of code that you don't use; especially useful for libraries.
Edit:
Even if you want to keep the default import and import the entire thing, imo. it makes more sense to use a simple Object rather than a class with static methods.
function getToken(username, password) {
//...
}
export default {
getToken
};
In you class definition add static in front of your function to be
class Auth {
static async getToken(username, password) {
const endpointOptions = {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username: `${username}`, password: `${password}` })
};
try {
const response = await fetch(`${Constant.BASE_WP_URL}${Constant.TOKEN_ENDPOINT}`, endpointOptions)
const data = await response.json();
if (!response.ok) {
const error = (data && data.message) || response.status;
throw error;
}
return data;
} catch (error) {
throw error
}
}
}
export default Auth;
then you will be able to call it as static function.
and requestSignIn will be using it in the following code
requestSignIn = async (event) => {
event.preventDefault();
this.setState({ loading: true })
try {
const authData = await Auth.getToken(`${this.state.email}`, `${this.state.password}`);
sessionStorage.setItem('authToken', authData.token)
} catch (error) {
console.log("Connection to WP - Auth Token failed ")
console.error(error);
}
}
I'm trying to get axios-auth-refresh working, to ensure that my requests always have a valid auth token, but as far as I can tell the Axios request isn't continuing after axios-auth-refresh intercepts it.
I'm pretty new to JS development, so not sure if I've missed something obvious. I've looked through the documentation, but can't see any major differences in my implementation.
I'm running Node v13.2.0, v2.2 (latest) of axios-auth-refresh, and v0.18.1 of axios
My code is as follows:
require('axios-debug-log');
const axios = require('axios');
const axiosauthrefresh = require('axios-auth-refresh');
const instance = axios.create({
baseURL: 'https://api.example.com/api/v1.0',
});
let authToken = '';
const refreshAuthLogic = (failedRequest) => {
console.log('Intercepting auth');
instance
.post('/auth/login/', {
username: process.env.USER,
password: process.env.PASS,
skipAuthRefresh: true,
})
.then((tokenRefreshResponse) => {
authToken = tokenRefreshResponse.data.token;
failedRequest.response.config.headers.Authorization = `Token ${authToken}`;
console.log(`Auth token: ${authToken}`);
return Promise.resolve();
});
};
function getAuthToken() {
if (authToken) {
console.log(`Token exists: ${authToken}`);
return `Token ${authToken}`;
}
return null;
}
instance.interceptors.request.use((request) => {
console.log(`Requesting ${request.url}`);
const token = getAuthToken();
if (token) {
request.headers.Authorization = token;
}
return request;
});
axiosauthrefresh.default(instance, refreshAuthLogic);
module.exports = {
instance,
};
I make a request like this:
// nmcapi.js
const request= require('./request');
async function GetFolderInfo(volumeID, filerID, path) {
try {
const refreshResponse = await request.instance.get(`/volumes/${volumeID}/filers/${filerID}/path/${path}`);
console.log(`Refresh triggered: ${path}`);
} catch (error) {
console.log(error);
}
}
// interval.js
const nmcapi = require('./nmcapi.js');
const info = await GetFolderInfo('examplevolumeid', 'examplefilerid', 'examplepath')
And this is what I get as output:
Requesting /volumes/examplevolumeid/filers/examplefilerid/path/examplepath
axios GET /volumes/examplevolumeid/filers/examplefilerid/path/examplepath +1ms
axios Error: Request failed with status code 401 (GET https://api.example.com/api/v1.0/volumes/examplevolumeid/filers/examplefilerid/path/examplepath) +265ms
Intercepting auth
Requesting /auth/login/
TypeError: Cannot read property 'then' of undefined
at f (/home/sean/data-reports/node_modules/axios-auth-refresh/dist/index.min.js:1:1718)
at /home/sean/data-reports/node_modules/axios-auth-refresh/dist/index.min.js:1:2719
at processTicksAndRejections (internal/process/task_queues.js:93:5)
at Object.GetFolderInfo (/home/sean/data-reports/server/nmcapi.js:29:29)
at /home/sean/data-reports/server/interval.js:25:18
at async Promise.all (index 0)
at Object.intervalFunc (/home/sean/data-reports/server/interval.js:36:18)
axios POST /auth/login/ +16ms
axios 200 OK (POST https://api.example.com/api/v1.0/auth/login/) +561ms
Auth token: 17412724ef5169eaab8502a9851480741e606ffa
As far as I can tell, the refreshAuthLogic function is working properly (because it returns a new auth token), but everything stops after that.
What am I missing?
I had missed an important point, the refreshAuthLogic function actually needs to return the axios instance instance.
The working implementation is:
const refreshAuthLogic = (failedRequest) => {
return instance
.post('/auth/login/', {
username: process.env.USER,
password: process.env.PASS,
skipAuthRefresh: true,
})
.then((tokenRefreshResponse) => {
failedRequest.response.config.headers.Authorization = `Token ${tokenRefreshResponse.data.token}`;
return Promise.resolve();
});
};
Thanks to Flyrell for answering this on GitHub