When I tried to check the condition for the response status, it is not even going inside and checking if, what should be done to compare and show the alert. FYI swal is a npm package used for alert
onSubmit(values) {
this.props.signIn(values, response => {
if (response.status === 200) {
swal("Login successful", "Signed In", "success");
this.props.history.push(`/dashboard/${response.data.user_name}`);
} else {
console.log("wrong password");
swal({
title: "Invalid Email Id or password",
icon: "warning"
});
}
});
};
Action.js
export function signIn(values, callback) {
const request = axios.post(`${ROOT_URL}/${APP_ID}/${API_KEY}/users/login`, values).then(() => callback());
return {
type: LOGIN_DETAILS,
payload: request
};
}
Need to return axios promise, try with that code
onSubmit(values) {
this.props
.signIn(values)
.then((response) => {
swal("Login successful", "Signed In", "success");
this.props.history.push(`/dashboard/${response.data.user_name}`);
})
.catch((error) => {
console.log("ajax error");
swal({
title: "Invalid Email Id or password",
icon: "warning",
});
});
}
first error is response.status will be undefined because you didn't pass response back in callback()
second one is if axios.post fail it will never call callback(), that mean the whole if else won't work.
To fix this you just need to change your action to this
export function signIn(values, callback) {
const request = axios.post(`${ROOT_URL}/${APP_ID}/${API_KEY}/users/login`, values).then(callback).catch(callback);
return {
type: LOGIN_DETAILS,
payload: request
};
}
or
export function signIn(values, callback) {
const request = axios.post(`${ROOT_URL}/${APP_ID}/${API_KEY}/users/login`, values).then((response) => callback(response)).catch((error) => callback(error));
return {
type: LOGIN_DETAILS,
payload: request
};
}
Related
When I click to delete a post, my console is saying TypeError: post.user.posts.deleteOne is not a function. It giving me this error after deleting.
const post = await Post.findByIdAndDelete(id).populate('user'); This code I am deleting the post from Post Schema
await post.user.posts.deleteOne(post)This code is to delete the post from the User Schema. I populated user and assigned it to post and then delete the user's post from this code, but I'm getting the error here.
Below is the controller code
export const deletePost = async (req, res) => {
const { id } = req.params;
try {
const post = await Post.findByIdAndDelete(id).populate('user');
await post.user.posts.deleteOne(post)
if (!post) {
return res.status(500).json({ message: "Unable To Delete" })
}
res.status(200).json({ message: "Deleted Successfully" })
} catch (error) {
console.log(error);
}
}
Client side delete request
const handleDeleteTrue = async () => {
try {
const { data } = await api.delete(`/post/${id}`)
console.log(data)
window.location.reload();
} catch (error) {
console.log(error.response.data.message);
}
};
User model schema
import mongoose from 'mongoose';
const userSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
username: {
type: String,
required: true,
unique: true
},
email: {
type: String,
required: true,
unqie: true
},
password: {
type: String,
required: true,
minlength: 6
},
posts: [{ type: mongoose.Types.ObjectId, ref: "Post", required: true }]
});
export default mongoose.model('User', userSchema);
Im able to delete the post from the post model schema, but in this pic, which shows the user model schema, that same post that was deleted is not deleted here. This is the problem Im trying to solve.
What I can seem to understand in your function below is that you're trying to delete a single post and also checking if post exists first
export const deletePost = async (req, res) => {
const { id } = req.params;
try {
const post = await Post.findByIdAndDelete(id).populate('user');
await post.user.posts.deleteOne(post)
if (!post) {
return res.status(500).json({ message: "Unable To Delete" })
}
res.status(200).json({ message: "Deleted Successfully" })
catch (error) {
console.log(error);
}
}
I'd suggest you try this
export const deletePost = async (req, res) => {
const { id } = req.params;
try {
//check if document exists in mongoDB collection
if (!mongoose.Types.ObjectId.isValid(id)) {
return res.status(500).json({ message: "Unable To Delete" }) }
await Post.deleteOne(id)
res.status(200).json({ message: "Deleted Successfully" })
catch (error) {
console.log(error);
}
}
I found out the answer. My user model schema for post was an array so I had to use $pull to delete it.
This is the code that worked for me
await post.user.posts.pull(post)
await post.user.save()
You can't use findOneAndDelete on populate to delete one object from an array. it doesn't work that way. Use This Instead.
const result = await User.findOneAndUpdate(
{ _id: Id },
{ $pull: { post:PostId } },
{ new: true }
);
You can find More on Pull and Push Operations on BlogLink
Code under test
// imports
const router = express.Router()
// This is what needs to be mocked
const client = new AwesomeGraphQLClient({
endpoint: process.env.GRAPHCMS_URL || '',
fetch,
fetchOptions: {
headers: {
authorization: `Bearer ${process.env.GRAPHCMS_TOKEN}`
}
}
})
interface LoginRequest {
email: string
password: string
}
router.post(
'/login',
async (req: Request<{}, {}, LoginRequest>, res: Response) => {
try {
const JWT_SECRET = getEnvironment('JWT_SECRET')
const { email, password } = req.body
if (!email || !password) {
res.status(400).json({
message: 'auth.provide.credentials',
full: 'You should provide an email and password'
})
return
}
if (!JWT_SECRET) {
res.status(500).json({
message: 'auth.secret.not.found',
full: 'Secret not found'
})
// TODO error logging
return
}
const { appUsers } = await client.request<
GetUserByEmailResponse,
GetUserByEmailVariables
>(getUserByEmailQuery, {
email
})
if (appUsers.length === 0) {
res.status(404).json({
message: 'auth.wrong.credentials',
full: 'You provided wrong credentials'
})
return
}
const user = appUsers[0]
const result: boolean = await bcrypt.compare(password, user.password)
if (result) {
var token = jwt.sign({ id: user.id, email: user.email }, JWT_SECRET)
res.status(200).json({
token
})
return
}
res.status(200).json({
message: 'auth.wrong.credentials',
full: 'You provided wrong credentials in the end'
})
} catch (e) {
console.log('E', e)
const error: ErrorObject = handleError(e)
res.status(error.code).json(error)
}
}
)
Tests for code above
import request from 'supertest'
import app from '../../../app'
import { mocked } from 'ts-jest/utils'
import { compare } from 'bcrypt'
import { AwesomeGraphQLClient } from 'awesome-graphql-client'
const mockRequestFn = jest.fn().mockReturnValue({
appUsers: [
{
id: 'tests'
}
]
})
jest.mock('awesome-graphql-client', () => ({
AwesomeGraphQLClient: jest.fn().mockImplementation(() => ({
request: mockRequestFn
}))
}))
I am trying to mock a method on a non default exported class from Awesome GraphQL. I also want to spy on this method, so I created a separate jest.fn() with a return value. The problem is that request is not a function: TypeError: client.request is not a function.
How can I mock and spy on the method of a mocked non default exported class?
SOLUTION
Managed to find a workaround. Make the method a function that returns the called mockRequest. This way you can spy on AwesomeGraphQLClient.request with mockRequest.toHaveBeenCalledTimes(x).
let mockRequest = jest.fn().mockReturnValue({
appUsers: [
{
id: 'tests'
}
]
})
jest.mock('awesome-graphql-client', () => {
return {
AwesomeGraphQLClient: jest.fn().mockImplementation(() => {
return {
request: () => mockRequest()
}
})
}
})
I was searching for a simple Login/Registration System and stumbled about this tutorial:
http://jasonwatmore.com/post/2017/09/16/react-redux-user-registration-and-login-tutorial-example
I tried it and added a real mongoDB Backend to remove the Mock-Backend. It was my first time doing Redux, but after a lot of struggle everything is working finde now! You can see the result here :
https://genko.de/login
Just one thing is not working at all and i have no clue how to solve it. Since I want to avoid bootstrap, I have a big problem with the error handling.
I want to display a snackbar from Material-Ui for following cases:
Username or Password is invalid
Successfull Logged in
Username is already taken (Registration)
In fact there are already some actions and reducers in my redux files, but to be honest, I just copied them from the tutorial and I have no clue how to work with them.
Following function is triggered by the Login button:
handleSubmit(e) {
e.preventDefault();
this.setState({ submitted: true });
const { username, password } = this.state;
const { dispatch } = this.props;
if (username && password) {
dispatch(userActions.login(username, password))
}
}
And this is my login function (user.actions):
function login(username, password) {
return dispatch => {
dispatch(request({ username }));
userService.login(username, password)
.then(
user => {
dispatch(success(user));
history.goBack();
},
error => {
dispatch(failure(error.toString()));
dispatch(alertActions.error(error.toString()));
}
);
};
function request(user) { return { type: userConstants.LOGIN_REQUEST, user } }
function success(user) { return { type: userConstants.LOGIN_SUCCESS, user } }
function failure(error) { return { type: userConstants.LOGIN_FAILURE, error } }
}
alert.actions:
import { alertConstants } from '../_constants';
export const alertActions = {
success,
error,
clear
};
function success(message) {
return { type: alertConstants.SUCCESS, message };
}
function error(message) {
return { type: alert, message };
}
function clear() {
return { type: alertConstants.CLEAR };
}
And finally my alert.constants:
export const alertConstants = {
SUCCESS: 'ALERT_SUCCESS',
ERROR: 'ALERT_ERROR',
CLEAR: 'ALERT_CLEAR'
};
Do you have some hints for me or suggestions?
Best regards :)
EDIT:
I forgot to show you my NodeJS Global Error Handler Middleware from this follow up tutorial to replace the face-backend:
module.exports = errorHandler;
function errorHandler(err, req, res, next) {
if (typeof (err) === 'string') {
// custom application error
return res.status(400).json({ message: err });
}
if (err.name === 'ValidationError') {
// mongoose validation error
return res.status(400).json({ message: err.message });
}
if (err.name === 'UnauthorizedError') {
// jwt authentication error
return res.status(401).json({ message: 'Invalid Token' });
}
// default to 500 server error
return res.status(500).json({ message: err.message });
}
you need to have an entries in your reducer for LOGIN_SUCCESS and LOGIN_FAILURE that will set the state in the redux store to something that you can use back in your component.
function reducer(state = { status: ''}, action) {
switch (action.type) {
case 'LOGIN_SUCCESS':
return { status: 'LOGGED_IN'}
... ETC
default:
return state
}
}
Then via mapStateToProps you will map the state of the redux store to props of the component and be able to use it like -
const mapStateToProps = (state) => ({
status: state.status
})
this.props.status // LOGGED_IN
I'm using a Redux Form to send a POST call to an Express endpoint. The endpoint is supposed to return the successfully saved object, or an error.
The endpoint successfully saves the posted data and returns the JSON. But Axios in the Redux action picks up both the .then and the .catch triggers-in the following action, it logs the following:
successful response: { …}
failure response: undefined
What am I doing wrong?
My Axios action:
export function addPlot(props) {
const user = JSON.parse(localStorage.getItem('user'));
return function(dispatch) {
axios
.post(
`${ROOT_URL}/plots`,
{
props
},
{ headers: { authorization: user.token } }
)
.then(response => {
console.log('successful response: ', response.data);
const plotModal = document.getElementById('plotModal');
plotModal.modal('dispose');
dispatch({ type: PLOT_ADDED, payload: response.data });
dispatch({ type: ADDING_PLOT, payload: false });
dispatch({
type: NEW_PLOT_GEOJSON,
payload: ''
});
})
.catch(response => {
console.log('failure response: ', response.data);
dispatch(authError(PLOT_ADD_FAILURE, 'Failed to add plot'));
});
}
My endpoint:
exports.newPlot = async (req, res, next) => {
console.log(JSON.stringify(req.body.props));
let company;
if (req.user.companyCode !== 'Trellis') {
company = req.user.companyCode;
} else {
company = req.body.props.company;
}
const {
name,
feature,
growerPhone,
plotCode,
rootStock,
region,
variety,
grower,
planted
} = req.body.props;
const plot = new Plot({
name,
grower,
variety,
planted,
region,
rootStock,
plotCode,
growerPhone,
feature,
company
});
try {
const newPlot = await plot.save();
res.json(newPlot);
} catch (e) {
console.log("couldn't save new plot", JSON.stringify(e));
return res.status(422).send({ error: { message: e, resend: true } });
}
};
You could use redux-thunk middleware to manage async actions.
The problem I see is that you are not dispatching the axios action, you must call dispatch(this.props.addPlot(props))in order to do something in the redux store.
So, along with Redux-Form I am using axios and thunk as middleware, and when I am in the onSubmit function (loginUser), and do AJAX calls with Axios. Unfortunately, when I want to signal that my user's submitted credentials are invalid and throw a SubmissionError to signal that the onSubmit function failed, and therefore get the errors displayed on the form I am getting "Uncaught (in promise)".
I have read from other threads that I might have to return a promise at some point, but I'm not entirely sure how to implement that (if that is even the problem).
Currently using version Redux-Form 6.5.0. Any help would be appreciated.
import axios from 'axios';
import { SubmissionError } from 'redux-form';
export function loginUser({ email, password }) {
return function(dispatch) {
axios.post(`${API_URL}/authenticate`, { email, password })
.then(response => {
console.log('status is: ', status, ' response is: ', response);
if(response.data.token){
cookie.save('token', response.data.token, { path: '/' });
dispatch({ type: AUTH_USER });
browserHistory.push('/');
} else {
if(response.data.success === false) {
var errObj = { email: "Invalid email and password combo", _error: "That email and password combination did not work. Please try again."};
throw (errObj)
}
}
})
.catch((error) => {
throw(new SubmissionError(error));
})
}
}
Error in console:
Uncaught (in promise) >
SubmissionError
errors
:
Object
message
:
"Submit Validation Failed"
name
:
"SubmissionError"
stack
:
"SubmissionError: Submit Validation Failed↵ at eval (eval at <anonymous> (http://localhost:8080/bundle.js:14:22671), <anonymous>:94:1297)"
__proto__
:
ExtendableError
For those of you wondering, I used 'resolve' and 'reject' with the SubmissionError inside of the reject() function as such (also notice the new Promise part at the top):
export function registerUser({ email, password }) {
return new Promise((resolve, reject) => {
axios.post('http://localhost:8088/api/users', { email: email, password: password })
.then(response => {
console.log('response is: ' , response, 'response.data is: ', response.data, 'response.code is: ', response.code);
if(response.data.success){
console.log('registerUser response.data.success is true')
cookie.save('token', response.data.token, { path: '/' });
store.dispatch({ type: AUTH_USER });
browserHistory.push('/');
resolve();
} else {
if(response.data.code === 11000){ //duplicate email
console.log('data code = 11000')
var errObj = new SubmissionError({_error: 'User registration failed, email already exists.' }) //need to add store dispatch for failed user registration (for form feedback)
reject(errObj);
} else if (response.code === 2) {
console.log('response.code = 2')
var errObj = new SubmissionError({ email: 'Invalid email pattern.' })
reject(errObj);
}
}
}).catch((error) => {
console.log('error is: ', error)
//errorHandler(store.dispatch, error, AUTH_ERROR)
if(error instanceof SubmissionError) reject(error);
});
})
}