Generator not catching error from API call - jestjs

I'm working with an API that doesn't return anything "error-like" for an incorrect login (just a normal object with a code -- incorrect_password or invalid_username -- an HTML message), so in my API calling function I'm turning the code into an error:
export async function loginWithApi(creds) {
try {
const res = await axios.get(ApiUrls.login, { params: creds });
if (res.data.data) {
return res.data;
} else if (res.data.code) {
throw Error(res.data.code.titleize());
} // else, it should be an actual error
} catch (err) {
return err;
}
}
This works correctly, throws correctly, returns an Error when it should.
However, when testing my saga generator action that calls this function, the error isn't being caught, it's going unabated through the try block:
function
export function* loginSaga({ username, password }) {
const creds = { username, password };
try {
const res = yield call(loginWithApi, creds);
console.log(res);
yield put({ type: "LOGIN_SUCCESS", user: res.data });
} catch (error) {
yield put({ type: "LOGIN_FAILURE", error });
}
}
tests
describe("login action", () => {
let gen;
afterEach(() => {
expect(gen.next().done).toBe(true);
});
it("should return a user object on a successful login", () => {
gen = loginSaga(success.creds);
gen.next(); // call api
expect(gen.next(mockResponse.success).value).toEqual(
put({ type: "LOGIN_SUCCESS", user: mockResponse.success.data })
);
});
it("should return an error when passed an invalid username", () => {
gen = loginSaga(badUser.creds);
gen.next(); // call api
// mockReponse.usernameError: Error("Invalid Username")
expect(gen.next(mockResponse.usernameError).value).toEqual(
put({ type: "LOGIN_FAILURE", error: "Invalid Username" })
);
});
it("should return an error when passed an invalid password", () => {
gen = loginSaga(badPw.creds);
gen.next(); // call api
// mockResponse.passwordError: Error("Incorrect Password"),
expect(gen.next(mockResponse.passwordError).value).toEqual(
put({ type: "LOGIN_FAILURE", error: "Incorrect Password" })
);
});
});
test output (excerpts)
● Saga Actions › login action › should return an error when passed an invalid username
expect(received).toEqual(expected) // deep equality
- Expected
+ Received
Object {
"##redux-saga/IO": true,
"combinator": false,
"payload": Object {
"action": Object {
- "error": "Invalid Username",
- "type": "LOGIN_FAILURE",
+ "type": "LOGIN_SUCCESS",
+ "user": undefined,
},
"channel": undefined,
},
"type": "PUT",
● Saga Actions › login action › should return an error when passed an invalid password
expect(received).toEqual(expected) // deep equality
- Expected
+ Received
Object {
"##redux-saga/IO": true,
"combinator": false,
"payload": Object {
"action": Object {
- "error": "Incorrect Password",
- "type": "LOGIN_FAILURE",
+ "type": "LOGIN_SUCCESS",
+ "user": undefined,
},
"channel": undefined,
},
"type": "PUT",
}
Logging confirms that the loginSaga function is receiving an Error in the second two tests. I can't quite determine why that error isn't being caught.

Finally got it.
So you need make generator think that Exception happened for one of its yield. To make that we have generator.prototype.throw().
So next code should work as expected:
it("should return an error when passed an invalid username", () => {
gen = loginSaga(badUser.creds);
gen.next(); // call api
// mockReponse.usernameError: Error("Invalid Username")
expect(gen.throw(mockResponse.usernameError).value).toEqual(
put({ type: "LOGIN_FAILURE", error: "Invalid Username" })
);
});

Related

Make a POST request and fetch posted data back to client

I need assistance in fetching data back to client as soon as it is posted to the database without reloading the page. The structure I have at present only shows the posts on page reload. Below are my codes.
const [pst, setPst] = useState([]);
const handleSubmit = async (e) => {
e.preventDefault();
const v2 = PST_REGEX.test(pst);
if (!v2) {
// setErrMsg("Invalid Entry");
return;
}
try {
const response = await axios
.post(DASHBOARD_URL, JSON.stringify({ pst }), {
headers: { "Content-Type": "application/json" },
withCredentials: true,
})
} catch (err) {
console.log(err)
}
};
Post Controller
const handleNewPost = async (req, res) => {
const { pst } = req.body;
if (!pst) return res.status(400).json({ message: "Field cannot be blank" });
try {
//create and store the new post
const result = await Userpost.create({
post: pst,
});
console.log(result);
res.status(201).json({ success: `New post ${pst} created!` });
} catch (err) {
res.status(500).json({ message: err.message });
}
};
There are some ways from which you can achieve your solution.
Method 1:
You can return newly created data from backend as success response. Lets assume you got response as follow:
{
"message": "Data created Successfully",
"status" : "201",
"data": [{
"id": "1234",
"title": "Blog title",
"description" : "Blog description"
}]
}
Then you can simply add response to you previous state as:
setPst([...pst, ...message.data]) // message.data is response from your endpoint
Method 2:
You can use socket io for real time experience: https://socket.io/

express post api isnt executing when sent string is 18 characters long

When i get the string from my discord bot, i make a post request to my api
axios.post(`http://localhost:8080/api/post/ban/${discord_id}`, {}, {
headers: {
key: key
}
}).then((response) => {
console.log(response.data)
})
But when its submitted the event isnt activated
When i sent string the length of 17 or less or length of 19 or more it worked but not when string length is 18
app.post('/api/post/ban/:discord_id/', async function (req, res) {
let id = req.params.discord_id
let header = req.headers;
if(isNaN(id)) return res.send({
"error": {
"message": "USER_ID_MUST_BE_NUMBER",
"code": "400"
}
});
if(id.length < 19 || id.length > 19) return res.send({
"error": {
"message": "ID_IS_NOT_VALID",
"code": "400"
}
});
if(header.key != key) return res.send({
"error": {
"message": "OWNER_ONLY",
"code": "none"
}
});
await banModel.findByIdAndUpdate(banID, {
$addToSet: { "bannedUsers": `${id}`}
});
return res.send({
"success": {
"message": "ADDED_USER_TO_BANS",
"code": "201"
}
});
});`
i fixed it heres the answer:
axios.post(`http://localhost:8080/api/post/ban/${discord_id}/`,{},{
headers: {
key: key
}
})
.then(function (response) {
console.log(response.data)
if(response.data.error) {
switch(response.data.error.code) {
case "404":
return interaction.reply("Channel not found!");
case "422":
return interaction.reply("Invalid parameters!");
case "400":
return interaction.reply("Invalid types of objects!");
case "409":
return interaction.reply("U already exist on our Database!");
case "none":
switch(response.data.error.message) {
case "INVALID_VIDEO_URL":
return interaction.reply("Invalid video url")
case "OWNER_ONLY":
return interaction.reply("Owner only API")
}
break;
}
}
if(response.data.success) return interaction.reply("Succesfully added your video!")
})
.catch(function (error) {
console.log(error);
});

AWS Lambda not calling third party api in loop

i am using aws lambda and i am calling third party server using request library. this library on outside the loop but on loop its not hitting even i am not getting any error or result. and i also checked on server there is no hitting
below is my code with console
exports.handler = async (event) => {
try {
let transactionList = [];
// event.body is json form object with keys and values that we send
userBrokers.forEach(async (brokers) => {
const userStocks = 50;
const qty = (qtyPercentage * userStocks) / 100;
console.log("function before api call")
request.post({
headers: {
'content-type': 'application/x-www-form-urlencoded',
"Apca-Api-Key-Id": brokers.api_key,
"Apca-Api-Secret-Key": brokers.secret_key
},
url: 'https://paper-api.alpaca.markets/v2/orders',
json: {
symbol,
qty,
side: orderType,
type: marketType,
time_in_force
}
}, function (error, response, body) {
if (error) {
console.log("api call error")
transactionList.push({
transactionSuccess: false,
reason: error,
userId: brokers.user_id,
documentId: brokers._id
})
}
else {
console.log("api response")
if (response.statusCode == 200) {
transactionList.push({
brokerOrderId: body.id,
symbol: body.symbol,
orderType: body.order_type,
marketType: body.side,
transactionSuccess: true,
userId: brokers.user_id,
documentId: brokers._id
})
}
else {
transactionList.push({
transactionSuccess: false,
reason: body.message,
userId: brokers.user_id,
documentId: brokers._id
})
}
}
});
})
console.log("function before response")
return {
statusCode: 200,
body: JSON.stringify(transactionList),
};
} catch (error) {
console.log("function catch block")
return {
statusCode: 200,
body: JSON.stringify(error.message),
};
}};
and i get below console.
function before api call
function before response
and response from lambda
status: 200 and message: []
The problem is that you're trying to execute an async method in a forEach but you have no way of enforcing Lambda to wait for the loop to finish. You should switch your forEach to a map and await all the promises:
Bad:
arr.forEach(async (val) => {})
Good:
const arrOfResults = await Promise.all(arr.map(async (val) => {}))

Stripe auth and capture charge with NODE.js

I'm having trouble with the pre-authorization described here https://stripe.com/docs/charges#auth-capture.
The auth goes successful (capture params to false), I can store the ID of the carghe on my databse and I can see the uncaptured charge from my Stripe Dashboard.
Problem comes when I try to capture the charge, because it fails with Error: No such charge: <CHARGE_ID>.
Here is the code:
constructor(){
this.stripe = require('stripe')('<sk_test_mysecretkey>');
}
async captureCharge(chargeId) {
try {
/**
* https://stripe.com/docs/api/charges/capture
*/
return this.stripe.charges.capture(chargeId)
.then((charge) => {
return {
error: false,
success: true,
message: 'ChargeSuccesful',
code: 200,
charge: charge,
};
},
(err) => {
console.log("CAPTURE CHARGE ERROR: ", err);
return {
success: false,
error: err.type,
code: err.statusCode,
};
}
);
} catch (e) {
console.log('ERROR CAPTURE', e);
}
}
Even if I try with a POST to https://api.stripe.com/v1/charges/<CHARGE_ID>/capture with auth: Bearer <sk_test_mysecretkey> i get the same error:
{
"error": {
"code": "resource_missing",
"doc_url": "https://stripe.com/docs/error-codes/resource-missing",
"message": "No such charge: <CHARGE_ID>",
"param": "charge",
"type": "invalid_request_error"
}
}
The uncaptured charge still exist on Stripe.
Thanks in advance.
UPDATE
I forgot to say that the charge isn't a simple charge, but it is a split payment with a shared customer. Stripe supports three approaches for processing split payment, I have choosed the direct charges:
async authorizationCharge(amount, merchantStripeId, billId, customerId) {
try {
var fee = 0.25;
/** Pre-Authorization Charge using a shared customer
* https://stripe.com/docs/charges#auth-capture
* https://stripe.com/docs/connect/shared-customers
*/
return this.stripe.tokens
.create(
{ customer: customerId },
{ stripe_account: merchantStripeId }
)
.then((oneTimeToken) => {
return this.stripe.charges
.create(
{
amount: Math.round(amount),
currency: 'eur',
application_fee: fee,
description: 'Bill ID: ' + billId,
source: oneTimeToken.id,
capture: false,
},
{ stripe_account: merchantStripeId }
)
.then((charge) => {
console.log('CHARGE: ', charge);
return {
error: false,
success: true,
code: 200,
charge: charge,
fee: fee,
};
},
(err) => {
// ERROR INFO:
// https://stripe.com/docs/api#error_handling
console.log('ERROR', err);
return {
success: false,
error: err.type,
code: err.statusCode,
};
}
);
},
(err) => {
// ERROR INFO:
// https://stripe.com/docs/api#error_handling
console.log('ERROR', err);
return { success: false, error: err.type, code: err.statusCode };
}
);
} catch (e) {
console.log(e);
}
}
Finally I understood the reason, I did not indicate the connected account (see the updated question).
return this.stripe.charges.capture(chargeId, { stripe_account: merchantStripeId }) <---- CONNECTED ACCOUNT
.then((charge) => {
...
}

GraphQL with RESTful returning empty response

I am connecting GraphQL with REST endpoints, I confirmed that whenever I am calling http://localhost:3001/graphql it is hitting REST endpoint and it is returning JSON response to GraphQL server, but I am getting an empty response from GraphQL server to GUI as follows:
{
"data": {
"merchant": {
"id": null
}
}
}
Query (decoded manually):
http://localhost:3001/graphql?query={
merchant(id: 1) {
id
}
}
Below is how my GraphQLObjectType looks like:
const MerchantType = new GraphQLObjectType({
name: 'Merchant',
description: 'Merchant details',
fields : () => ({
id : {
type: GraphQLString // ,
// resolve: merchant => merchant.id
},
email: {type: GraphQLString}, // same name as field in REST response, so resolver is not requested
mobile: {type: GraphQLString}
})
});
const QueryType = new GraphQLObjectType({
name: 'Query',
description: 'The root of all... queries',
fields: () => ({
merchant: {
type: merchant.MerchantType,
args: {
id: {type: new GraphQLNonNull(GraphQLID)},
},
resolve: (root, args) => rest.fetchResponseByURL(`merchant/${args.id}/`)
},
}),
});
Response from REST endpoint (I also tried with single object in JSON instead of JSON array):
[
{
"merchant": {
"id": "1",
"email": "a#b.com",
"mobile": "1234567890"
}
}
]
REST call using node-fetch
function fetchResponseByURL(relativeURL) {
return fetch(`${config.BASE_URL}${relativeURL}`, {
method: 'GET',
headers: {
Accept: 'application/json',
}
})
.then(response => {
if (response.ok) {
return response.json();
}
})
.catch(error => { console.log('request failed', error); });
}
const rest = {
fetchResponseByURL
}
export default rest
GitHub: https://github.com/vishrantgupta/graphql
JSON endpoint (dummy): https://api.myjson.com/bins/8lwqk
Edit: Adding node.js tag, may be issue with promise object.
Your fetchResponseByURL function get empty string.
I think the main problem is that you are using wrong function to get the your JSON string, please try to install request-promise and use it to get your JSON string.
https://github.com/request/request-promise#readme
something like
var rp = require('request-promise');
function fetchResponseByURL(relativeURL) {
return rp('https://api.myjson.com/bins/8lwqk')
.then((html) => {
const data = JSON.parse(html)
return data.merchant
})
.catch((err) => console.error(err));
// .catch(error => { console.log('request failed', error); });
}
In this case using data.merchant solved my problem. But the above suggested solution i.e., use of JSON.parse(...) might not be the best practice because if there are no object in JSON, then expected response might be as follows:
{
"data": {
"merchant": null
}
}
Instead of fields to be null.
{
"data": {
"merchant": {
"id": null // even though merchant is null in JSON,
// I am getting a merchant object in response from GraphQL
}
}
}
I have updated my GitHub: https://github.com/vishrantgupta/graphql with working code.

Resources