Sunday 16 July 2023

React CRUD application with JWT Authentication and file upload or download + Spring Boot API

In this tutorial, we’re gonna build a React CRUD (Create, Read, Update, Delete) Application with JWT Authentication, File upload/ download, and consume CRUD Restful APIs developed by Spring boot.

  • JWT Authentication Flow for User Signup & User Login.
  • Project Structure for React JWT Authentication (without Redux) with LocalStorage, React Router, Axios, react-bootstrap, react-hook-form.
  • Creating React Components with Form Validation.
  • Dynamic Navigation Bar in React App.

Let's start with creating a React App using create-react-app CLI.

To create a new app, you may choose one of the following methods:

Using npx
npx create-react-app react-appname

Using npm
npm init react-app react-appname












Install some npm packages:

npm install bootstrap reactstrap
npm install react-bootstrap bootstrap
npm install reactstrap react react-dom
npm install axios
npm install react-router-dom
npm install react-hook-form











------------------------------packege.json--------------------------------
{
  "name": "react-hooks-jwt-auth",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@testing-library/jest-dom": "^5.14.1",
    "@testing-library/react": "^11.2.7",
    "@testing-library/user-event": "^12.8.3",
    "axios": "^0.26.1",
    "bootstrap": "^4.6.2",
    "react": "^17.0.2",
    "react-bootstrap": "^2.8.0",
    "react-dom": "^17.0.2",
    "react-hook-form": "^7.45.1",
    "react-router-dom": "^6.14.1",
    "react-scripts": "4.0.3",
    "react-validation": "^3.0.7",
    "reactstrap": "^9.2.0",
    "validator": "^13.0.0"
  },
  "scripts": {
    "start": "react-scripts --openssl-legacy-provider start",
    "build": "react-scripts --openssl-legacy-provider build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": "react-app"
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}


------------------------------app.js--------------------------------

import React, { useState, useEffect } from "react";
import { Routes, Route, Link } from "react-router-dom";
import "bootstrap/dist/css/bootstrap.min.css";
import "./App.css";

import AuthService from "./services/auth.service";

import Login from "./components/Login";
import Register from "./components/Register";
import Home from "./components/Home";
import Profile from "./components/Profile";
import BoardUser from "./components/BoardUser";
import BoardModerator from "./components/BoardModerator";
import BoardAdmin from "./components/BoardAdmin";
import FormSubmit from "./components/FormSubmit";
import EventBus from "./common/EventBus";
import FormDataFetch from "./components/FormDataFetch";
import FormDtailView from "./components/FormDetailView";
import FormDetailEdit from "./components/FormDetailEdit";

const App = () => {
  const [showModeratorBoard, setShowModeratorBoard] = useState(false);
  const [showAdminBoard, setShowAdminBoard] = useState(false);
  const [currentUser, setCurrentUser] = useState(undefined);

  useEffect(() => {
    const user = AuthService.getCurrentUser();

    if (user) {
      setCurrentUser(user);
      setShowModeratorBoard(user.roles.includes("ROLE_MODERATOR"));
      setShowAdminBoard(user.roles.includes("ROLE_ADMIN"));
    }

    EventBus.on("logout", () => {
      logOut();
    });

    return () => {
      EventBus.remove("logout");
    };
  }, []);

  const logOut = () => {
    AuthService.logout();
    setShowModeratorBoard(false);
    setShowAdminBoard(false);
    setCurrentUser(undefined);
  };

  return (
    <div>
      <nav className="navbar navbar-expand navbar-dark bg-dark">
        <Link to={"/"} className="navbar-brand">
          Ramsis Code
        </Link>
        <div className="navbar-nav mr-auto">
          <li className="nav-item">
            <Link to={"/home"} className="nav-link">
              Home
            </Link>
          </li>

          {showModeratorBoard && (
            <li className="nav-item">
              <Link to={"/mod"} className="nav-link">
                Moderator Board
              </Link>
            </li>
          )}

          {showAdminBoard && (
            <li className="nav-item">
              <Link to={"/admin"} className="nav-link">
                Admin Board
              </Link>
            </li>
          )}

          {currentUser && (
            <li className="nav-item">
              <Link to={"/user"} className="nav-link">
                User
              </Link>
            </li>
          )}

        {currentUser && (
           <li className="nav-item">
              <Link to={"/formDataFetch"} className="nav-link">
                Record Fetch
              </Link>
            </li>
        )}    
        </div>

        {currentUser ? (
          <div className="navbar-nav ml-auto">
            <li className="nav-item">
              <Link to={"/profile"} className="nav-link">
                {currentUser.username}
              </Link>
            </li>
            <li className="nav-item">
              <a href="/login" className="nav-link" onClick={logOut}>
                LogOut
              </a>
            </li>
          </div>
        ) : (
          <div className="navbar-nav ml-auto">
            <li className="nav-item">
              <Link to={"/login"} className="nav-link">
                Login
              </Link>
            </li>

            <li className="nav-item">
              <Link to={"/register"} className="nav-link">
                Sign Up
              </Link>
            </li>
          </div>
        )}
      </nav>

      <div className="container mt-3">
        <Routes>
          <Route path="/" element={<Home/>} />
          <Route path="/home" element={<Home/>} />
          <Route path="/login" element={<Login/>} />
          <Route path="/register" element={<Register/>} />
          <Route path="/profile" element={<Profile/>} />
          <Route path="/user" element={<BoardUser/>} />
          <Route path="/mod" element={<BoardModerator/>} />
          <Route path="/admin" element={<BoardAdmin/>} />
          <Route path="/formDataFetch" element={<FormDataFetch/>} />
          <Route path="/formFill" element={<FormSubmit/>} />
          <Route path="/formFill" element={<FormSubmit/>} />
          <Route path="/formDetailView/:id" element={<FormDtailView/>} />
          <Route path="/formDetailEdit/:id" element={<FormDetailEdit/>} />
        </Routes>
      </div>

    </div>
  );
};

export default App;


-------------------------------------auth.service.js----------------------------------------

import axios from "axios";

const API_URL = "http://localhost:8080/api/auth/";

const register = (username, email, password) => {
  return axios.post(API_URL + "signup", {
    username,
    email,
    password,
  });
};

const login = (username, password) => {
  alert(username +" "+password);
  return axios
    .post(API_URL + "signin", {
      username,
      password,
    })
    .then((response) => {
      if (response.data.accessToken) {
        localStorage.setItem("user", JSON.stringify(response.data));
        localStorage.setItem("facilityId", "NHA12039");
        localStorage.setItem("facilityName", "Kalesh Hospital Delhi");
        localStorage.setItem("Role", "Hospital");

      }

      return response.data;
    });
};

const logout = () => {
  localStorage.removeItem("user");
};

const getCurrentUser = () => {
  return JSON.parse(localStorage.getItem("user"));
};

const AuthService = {
  register,
  login,
  logout,
  getCurrentUser,
};

export default AuthService;



------------------------------------------ auth-header.js---------------------------------------

export default function authHeader() {
  const user = JSON.parse(localStorage.getItem('user'));

  if (user && user.accessToken) {
    return { Authorization: 'Bearer ' + user.accessToken }; // for Spring Boot back-end
    // return { 'x-access-token': user.accessToken };       // for Node.js Express back-end
  } else {
    return {};
  }
}


-------------------------------------user.service.js--------------------------------------

import axios from "axios";
import authHeader from "./auth-header";

const API_URL = "http://localhost:8080/api/test/";
const APP_URL = "http://localhost:8080/api/app/";

const getPublicContent = () => {
  return axios.get(API_URL + "all");
};

const getUserBoard = () => {
  return axios.get(API_URL + "user", { headers: authHeader() });
};

const getModeratorBoard = () => {
  return axios.get(API_URL + "mod", { headers: authHeader() });
};

const getAdminBoard = () => {
  return axios.get(API_URL + "admin", { headers: authHeader() });
};

const getFormData = () => {
  return axios.get(API_URL + "formData", { headers: authHeader() });
};

const getFormDataList = () => {
  return axios.get(APP_URL + "getFormData", { headers: authHeader() });
};

const getFormDataById = (id) => {
  return axios.get(APP_URL + "getFormDataById/"+id, { headers: authHeader() });
};

const saveForm = (userObject) =>{
 //console.log(userObject.name);
  const user = JSON.parse(localStorage.getItem('user'));
  return axios.post(APP_URL +"formInsert",userObject,
  { headers:{
    "Content-Type": "multipart/form-data",
    "Authorization" : 'Bearer '+user.accessToken,
  }}
  );
}

const updateForm = (userObject,id) =>{
   const user = JSON.parse(localStorage.getItem('user'));
   return axios.put(APP_URL +"formUpdate/"+id,userObject,
   { headers:{
     "Content-Type": "multipart/form-data",
     "Authorization" : 'Bearer '+user.accessToken,
   }}
   );
 }

 const deleteUser = (id) => {
  return axios.delete(APP_URL + "deleteUser/"+id, { headers: authHeader() });
};

const getFile = () => {
  return axios.get(APP_URL + "files", { headers: authHeader() });
};

const UserService = {
  getPublicContent,
  getUserBoard,
  getModeratorBoard,
  getAdminBoard,
  getFormData,
  saveForm,
  getFormDataList,
  getFormDataById,
  updateForm,
  deleteUser,
  getFile,
  APP_URL,
 
};

export default UserService;



--------------------------------- FormDetailFetch.js------------------------------

import { useState, useEffect} from "react";
import { Routes, Route, Link } from "react-router-dom";
import FormSubmit from "./FormSubmit";
import UserService from "../services/user.service";
import Table from 'react-bootstrap/Table';
import EventBus from "../common/EventBus";
import FormDtailView from "./FormDetailView";
import FormDetailEdit from "./FormDetailEdit";

const FormDataFetch = () => {
const [users, setUsers] = useState([]);
const [message,setMessage] = useState("");

   const userList = () =>{
    UserService.getFormDataList().then(
        (response) => {
          console.log(response.data);
          setUsers(response.data);
        },
        (error) => {
          if (error.response===401 || error.response.data.status === 401) {
            EventBus.dispatch("logout");
          }
        }
      );
   }  


   useEffect(()=> {
    userList();
   },[])

   const deleteUser = async(id) => {
        const response = await UserService.deleteUser(id)
        userList();
        setMessage(response.data.message+" user id :"+id);
   }

   return<>
    <Link to={"/formFill"} className="badge badge-primary">
        Form Registration
    </Link>  
    <div className="container">
    <h5>User List</h5>
    {message &&(<div className="alert alert-success">
     {message}
    </div>)}
     <Table striped bordered hover>
        <thead>
           <th>Id</th>
           <th>Name</th>
           <th>Email</th>
           <th>Department</th>
           <th>Phone</th>
           <th>City</th>
           <th>Download</th>
           <th>Image</th>
           <th>View</th>
           <th>Edit</th>
           <th>Delete</th>
        </thead>
        <tbody>
        {
        users.map((user,index)=>(
               
            <tr>  
               <td>{index+1}</td>                
               <td>{user.name}</td>
               <td>{user.email}</td>
               <td>{user.department}</td>
               <td>{user.phone}</td>
               <td>{user.city}</td>
               <td>  
               <a href={user.url}>{user.fileName}</a>
              </td>
              <td><img src={user.url} width="60" height="40" alt={user.fileName}></img></td>
               <td><Link className="btn btn-primary" to={"/formDetailView/"+user.id}>view</Link></td>
               <td><Link className="btn btn-primary" to={"/formDetailEdit/"+user.id}>Edit</Link></td>
               <td><Link className="btn btn-danger" onClick={()=>deleteUser(user.id)}>Delete</Link></td>
            </tr>
        ))
        }
        </tbody>
     </Table>


    <div className="container mt-3">
        <Routes>
         <Route path="/formFill" element={<FormSubmit/>} />
         <Route path="/formDetailView/:id" element={<FormDtailView/>} />
         <Route path="/formDetailEdit/:id" element={<FormDetailEdit/>} />
        </Routes>
    </div>
    </div>  
    </>
}
export default FormDataFetch;


-----------------------------------FormSubmit.js-------------------------------

import React, { useState, useEffect } from "react";
import UserService from "../services/user.service";
import EventBus from "../common/EventBus";
import Form from 'react-bootstrap/Form';


const FormSubmit = () => {
  const [content, setContent] = useState("");

  useEffect(() => {
    UserService.getFormData().then(
      (response) => {
        setContent(response.data);
      },
      (error) => {
        const _content =
          (error.response &&
            error.response.data &&
            error.response.data.message) ||
          error.message ||
          error.toString();

        setContent(_content);

        if (error.response && error.response.status === 401) {
          EventBus.dispatch("logout");
        }
      }
    );
  }, []);

  const onChangePicture = e => {
    console.log('selectFile: ', selectFile);
    setSelectFile(e.target.files[0]);
};

  const [name,setName] = useState("");
  const [email,setEmail] = useState("");
  const [department,setDepartment] = useState("");
  const [phone,setPhone] = useState("");
  const [city,setCity] = useState("");
  const [selectFile, setSelectFile] = useState("");
  const [msg, setMessage] = useState("");
  const [msgName, setMsgName] = useState("");
  const [msgEmail, setMsgEmail] = useState("");
  const [msgDept, setMsgDept] = useState("");
  const [msgCity, setMsgCity] = useState("");
  const [msgPhone, setMsgPhone] = useState("");
  const [msgFile, setMsgFile] = useState("");
 
  const validate = () => {
    let returnError=false;
    const regexEmail = /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/i;

    if(name===''){
      setMsgName("Name is Required.");
      returnError=true;
    }else{
      setMsgName('');
      returnError=false;
    }

    if(email===''){
      setMsgEmail("Email is Required.");
      returnError=true;
    }else if(!regexEmail.test(email)){
      setMsgEmail("Invalid Email.");
      returnError=true;
    } else{
      setMsgEmail('');
      returnError=false;
    }
   

    if(department===''){
      setMsgDept("Department is Required.");
      returnError=true;
    }else{
      setMsgDept('');
      returnError=false;
    }

    if(phone===''){
      setMsgPhone("Phone is Required.");
      returnError=true;
    }else{
      setMsgPhone('');
      returnError=false;
    }
   
    if(city===''){
      setMsgCity("City is Required.");
      returnError=true;
    }else{
      setMsgCity('');
      returnError=false;
    }

    if(selectFile===''){
      setMsgFile("File is Required.");
      returnError=true;
    }else{
      setMsgFile('');
      returnError=false;
    }

    return returnError;

  }
 
  const submitForm = (e) =>{
    e.preventDefault();

    const returnError = validate();  

    if(returnError===false){

          const userObject = new FormData();
          userObject.append("name",name);
          userObject.append("email",email);
          userObject.append("department",department);
          userObject.append("phone",phone);
          userObject.append("city",city);
          userObject.append("selectFile",selectFile);

          UserService.saveForm(userObject)
       
          .then((response)=>{
            if(response.status===200){
             // setMessage(response.data.message);
              alert(response.data.message);
              setName('');
              setEmail('');
              setDepartment('');
              setPhone('');
              setCity('');
              setSelectFile('');
            }
          })
          .catch((error)=>{
            console.log(error);
          setMessage(error.response.data.message);
          })
        }
   }


  return (
    <div className="container">
     <div className="row"></div>
      <header className="jumbotron">
       
    <Form onSubmit={submitForm}>
      <h5>{content}</h5>
        {msg &&(<div className="alert alert-success">
         {msg}
       </div>)}
      <div className="form-group">  
      <label className="form-label">Name</label>
      <input type="text" placeholder="name" value={name} onChange={(e)=> setName(e.target.value)} />
      {msgName && <p style={{color: "red"}}>{msgName}</p>}
     </div>
     <div className="form-group">
     <label className="form-label">Email address</label>
      <input type="email" placeholder="name@example.com"  value={email}
onChange={(e)=> setEmail(e.target.value)} />
      {msgEmail && <p style={{color: "red"}}>{msgEmail}</p>}
    </div>
    <div className="form-group">
     <label className="form-label">Department</label>
      <input type="text" placeholder="Department"  value={department}
onChange={(e)=> setDepartment(e.target.value)} />
      {msgDept && <p style={{color: "red"}}>{msgDept}</p>}
    </div>
    <div className="form-group">
    <label className="form-label">Phone</label>
      <input type="text"  placeholder="phone" value={phone} onChange={(e)=> setPhone(e.target.value)} />
      {msgPhone && <p style={{color: "red"}}>{msgPhone}</p>}
     </div>
     <div className="form-group">
     <label className="form-label">City</label>
      <input type="text" placeholder="city"   value={city} onChange={(e)=> setCity(e.target.value)} />
      {msgCity && <p style={{color: "red"}}>{msgCity}</p>}
     </div>

     <div className="form-group">
     <label className="form-label">Upload</label>
      <input type="file"  onChange={onChangePicture} />
      {msgFile && <p style={{color: "red"}}>{msgFile}</p>}
     </div>
     <div className="form-group">
      <button className="btn btn-primary">Submit</button>
      </div>
     </Form>  
     </header>
    </div>
  );
};

export default FormSubmit;



---------------------------------FormDetailEdit.js------------------------------------

import React, { useState, useEffect } from "react";
import UserService from "../services/user.service";
import Form from 'react-bootstrap/Form';
import { useParams } from "react-router-dom";
import { useForm } from "react-hook-form";

const FormDetailEdit = () =>{

  const {register,handleSubmit,formState: { errors },} = useForm();

    const {id} = useParams();
    const [name,setName] = useState("");
    const [email,setEmail] = useState("");
    const [department,setDepartment] = useState("");
    const [phone,setPhone] = useState("");
    const [city,setCity] = useState("");
    const [message,setMessage] = useState("");
    const [selectFile, setSelectFile] = useState("");
   const loadUserListById= ()=>{

        UserService.getFormDataById(id).then(
            (response) => {
            const {name,department,email,phone,city} = response.data;
            setName(name);
            setEmail(email);
            setDepartment(department);
            setPhone(phone);
            setCity(city);
            console.log(email);
            },
            (error) => {
                setMessage(error.response.data.message);
            }
          );

    }

    useEffect(()=> {
        loadUserListById();
    },[])


    const updateForm = (e) =>{
       // e.preventDefault();
     
        const userObject = new FormData();
        userObject.append("name",name);
        userObject.append("email",email);
        userObject.append("department",department);
        userObject.append("phone",phone);
        userObject.append("city",city);
        userObject.append("selectFile",selectFile);

      UserService.updateForm(userObject,id)
    .then((response)=>{
       if(response.status===200){
        setMessage(response.data.message);
       }
    })
    .catch((error)=>{
      console.log(error);
     setMessage(error.response.data.message);
    })
  }

  const onChangePicture = e => {
    console.log('selectFile: ', selectFile);
    setSelectFile(e.target.files[0]);
};

    return <>
    <div className="container">
     <div className="row"></div>
      <header className="jumbotron">
        <h5>User Edit</h5>
        {message &&(<div className="alert alert-success">
         {message}
       </div>)}
      <Form onSubmit={handleSubmit (updateForm)}>
      <div className="form-group">  
       <label className="form-label">Name</label>
        <input  {...register("name", { required: "Name is required" })}
placeholder="name" value={name} type="text" onChange={(e)=> setName(e.target.value)} />
        {errors.name?.message && <p style={{color: "red"}}>{errors.name?.message}  </p>}
      </div>
     
      <div className="form-group">  
        <label className="form-label">Email address</label>
        <input  {...register("email", { required: "Email is required" ,
 pattern:{value: /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/i, message: "Invalid email." }})}
type="text" placeholder="name@example.com" value={email} onChange={(e)=>
setEmail(e.target.value)} />
        {errors.email?.message && <p style={{color: "red"}}>{errors.email?.message}</p>}
      </div>

      <div className="form-group">  
        <label className="form-label">Department</label>
        <input {...register("department", { required: "Department is required" })}
type="text" placeholder="Department" value={department} onChange={(e)=> setDepartment(e.target.value)} />
        {errors.department?.message && <p style={{color: "red"}}>{errors.department?.message}  </p>}
      </div>

      <div className="form-group">  
        <label className="form-label">Phone</label>
        <input {...register("phone", { required: "Phone is required" })}  
type="text" placeholder="phone" value={phone} onChange={(e)=> setPhone(e.target.value)} />
        {errors.phone?.message && <p style={{color: "red"}}>{errors.phone?.message}  </p>}
      </div>

      <div className="form-group">  
        <label className="form-label">City</label>
        <input {...register("city", { required: "City is required" })}  type="text"
placeholder="city" value={city} onChange={(e)=> setCity(e.target.value)} />
        {errors.city?.message && <p style={{color: "red"}}>{errors.city?.message}  </p>}
      </div>

      <div className="form-group">  
        <label className="form-label">Upload</label>
        <input {...register("selectFile", { required: "File is required" })}
type="file" onChange={onChangePicture} />
        {errors.selectFile?.message && <p style={{color: "red"}}>
{errors.selectFile?.message}  </p>}
      </div>
      <div className="form-group">  
      <button className="btn btn-primary">Update</button>
      </div>
     </Form>  
      </header>
    </div>
    </>
}

export default FormDetailEdit;

--------------------------------FormDetailView.js-------------------------------


import { useState, useEffect} from "react";
import { useParams } from "react-router-dom";
import UserService from "../services/user.service";
import EventBus from "../common/EventBus";

const FormDtailView = () => {

    const {id} = useParams();
    const [name,setName] = useState("");
    const [email,setEmail] = useState("");
    const [department,setDepartment] = useState("");
    const [phone,setPhone] = useState("");
    const [city,setCity] = useState("");
    const [message,setMessage] = useState("");
    const fileUrl = UserService.APP_URL+"files/"+id;
     const loadUserListById= ()=>{
        UserService.getFormDataById(id).then(
            (response) => {
            const {name,department,email,phone,city} = response.data;
            setName(name);
            setEmail(email);
            setDepartment(department);
            setPhone(phone);
            setCity(city);
            console.log(email);
            },
            (error) => {
                setMessage(error.response.data.message);
                if (error.response===401 || error.response.data.status === 401) {
                    EventBus.dispatch("logout");
                  }
            }
          );

    }

    useEffect(()=> {
        loadUserListById();
    },[])

    return <>
    <h2>Detail View</h2>
     <h5>{message}</h5>
    Name : {name} <br/>
    Email :{email} <br/>
    Department :{department} <br/>
    Phone :{phone} <br/>
    City :{city} <br/><br/>
    <div>
    <img src={fileUrl} height="200" width="200"></img>
    </div>
    </>
}
export default FormDtailView;


------------------------------Register.js-------------------------------

import React, { useState, useRef } from "react";
import Form from "react-validation/build/form";
import Input from "react-validation/build/input";
import CheckButton from "react-validation/build/button";
import { isEmail } from "validator";

import AuthService from "../services/auth.service";

const required = (value) => {
  if (!value) {
    return (
      <div className="alert alert-danger" role="alert">
        This field is required!
      </div>
    );
  }
};

const validEmail = (value) => {
  if (!isEmail(value)) {
    return (
      <div className="alert alert-danger" role="alert">
        This is not a valid email.
      </div>
    );
  }
};

const vusername = (value) => {
  if (value.length < 3 || value.length > 20) {
    return (
      <div className="alert alert-danger" role="alert">
        The username must be between 3 and 20 characters.
      </div>
    );
  }
};

const vpassword = (value) => {
  if (value.length < 6 || value.length > 40) {
    return (
      <div className="alert alert-danger" role="alert">
        The password must be between 6 and 40 characters.
      </div>
    );
  }
};

const Register = () => {
  const form = useRef();
  const checkBtn = useRef();

  const [username, setUsername] = useState("");
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  const [successful, setSuccessful] = useState(false);
  const [message, setMessage] = useState("");

  const onChangeUsername = (e) => {
    const username = e.target.value;
    setUsername(username);
  };

  const onChangeEmail = (e) => {
    const email = e.target.value;
    setEmail(email);
  };

  const onChangePassword = (e) => {
    const password = e.target.value;
    setPassword(password);
  };

  const handleRegister = (e) => {
    e.preventDefault();

    setMessage("");
    setSuccessful(false);

    form.current.validateAll();

    if (checkBtn.current.context._errors.length === 0) {
      AuthService.register(username, email, password).then(
        (response) => {
          setMessage(response.data.message);
          setSuccessful(true);
        },
        (error) => {
          const resMessage =
            (error.response &&
              error.response.data &&
              error.response.data.message) ||
            error.message ||
            error.toString();

          setMessage(resMessage);
          setSuccessful(false);
        }
      );
    }
  };

  return (
    <div className="col-md-12">
      <div className="card card-container">
        <img
          src="//ssl.gstatic.com/accounts/ui/avatar_2x.png"
          alt="profile-img"
          className="profile-img-card"
        />

        <Form onSubmit={handleRegister} ref={form}>
          {!successful && (
            <div>
              <div className="form-group">
                <label htmlFor="username">Username</label>
                <Input
                  type="text"
                  className="form-control"
                  name="username"
                  value={username}
                  onChange={onChangeUsername}
                  validations={[required, vusername]}
                />
              </div>

              <div className="form-group">
                <label htmlFor="email">Email</label>
                <Input
                  type="text"
                  className="form-control"
                  name="email"
                  value={email}
                  onChange={onChangeEmail}
                  validations={[required, validEmail]}
                />
              </div>

              <div className="form-group">
                <label htmlFor="password">Password</label>
                <Input
                  type="password"
                  className="form-control"
                  name="password"
                  value={password}
                  onChange={onChangePassword}
                  validations={[required, vpassword]}
                />
              </div>

              <div className="form-group">
                <button className="btn btn-primary btn-block">Sign Up</button>
              </div>
            </div>
          )}

          {message && (
            <div className="form-group">
              <div
                className={
                  successful ? "alert alert-success" : "alert alert-danger"
                }
                role="alert"
              >
                {message}
              </div>
            </div>
          )}
          <CheckButton style={{ display: "none" }} ref={checkBtn} />
        </Form>
      </div>
    </div>
  );
};

export default Register;



Spring Boot API






Download Code


 

Saturday 15 July 2023

Spring Boot MVC CRUD Application with MySQL and JPA Repository

This is a very simple and basic introductory Application to start with Spring Boot MVC CRUD Application with MySQL and JPA Repository. This project gives an idea about how to create Spring Boot MVC Full Application having Model, Controller and View binded together to perform CRUD operations with Database connection using DAO Classes and JPA Repository.

You can download Code at the end.

Registration Page


Employee List


Employee Edit


Project Structure


---------------------SpringBootMVCApplication--------------------------

package com.ramsis.main;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.view.InternalResourceViewResolver;

@SpringBootApplication
public class SpringBootMVCApplication {

    public static void main(String[] args) throws Exception {
        SpringApplication.run(SpringBootMVCApplication.class, args);
    }
   
    @Bean
    public ViewResolver viewResolver() {
         final InternalResourceViewResolver r = new InternalResourceViewResolver();
         r.setPrefix("/WEB-INF/jsp/");
         r.setSuffix(".jsp");
         return r;
    }
}

---------------------------CORSFilter----------------------------

package com.ramsis.main.controller;

import javax.servlet.*;
import java.io.IOException;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;

@Component
public class CORSFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
System.out.println("CORSFilter");
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, GET");
response.setHeader("Access-Control-Max-Age", "3700");
response.setHeader("Access-Control-Allow-Headers", "Content-Type");
response.setHeader("Access-Control-Allow-Credentials", "true");
chain.doFilter(req, res);
}

public void init(FilterConfig filterConfig) {
}
public void destroy() {
}
}

--------------------------RegistrationController-----------------------

package com.ramsis.main.controller;

import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import com.ramsis.main.dto.EmployeeDTO;
import com.ramsis.main.service.EmployeeService;

@Controller
@CrossOrigin(origins = "*")
public class RegistrationController {

@Autowired
private EmployeeService employeeService;

@GetMapping("/registration")
public String reg(Map<String, Object> model) {
model.put("employee", new EmployeeDTO());
return "Registration";
}

@PostMapping("/home")
public String createEmployee(@ModelAttribute("employee") EmployeeDTO empDto) {
employeeService.createOrUpdateEmployee(empDto);
return "redirect:/list";
}

@GetMapping("/list")
public String listOfEmployee(Model model) {
List<EmployeeDTO> employeeList = employeeService.getAllEmployee();
model.addAttribute("empList", employeeList);
return "employeeList";
}

@PostMapping("/delete")
public String deleteEmployee(@RequestParam("id") String id) {
employeeService.deleteEmployee(Long.parseLong(id));
return "redirect:/list";
}

@GetMapping("/edit")
public String editEmployee(@RequestParam("id") String id, Map<String, Object> model) {
EmployeeDTO empDTO = employeeService.editEmployee(Long.parseLong(id));
model.put("employee", empDTO);
return "Registration";
}

}

-------------------------------EmployeeDTO-----------------------------
package com.ramsis.main.dto;

import com.ramsis.main.model.Employee;

public class EmployeeDTO {

private Long id;
private String firstName;
private String lastName;
private String userName;
private String emailId;
private String empId;
private String bloodGp;
private int age;
private String personalEmail;
private String mobileNo;

public EmployeeDTO() {
}

public EmployeeDTO(Employee employee) {
this.firstName = employee.getFirstName();
this.lastName = employee.getLastName();
this.userName = employee.getUserName();
this.emailId = employee.getEmailId();
this.empId = employee.getEmpId();
this.bloodGp = employee.getBloodGp();
this.age = employee.getAge();
this.personalEmail = employee.getPersonalEmail();
this.mobileNo = employee.getMobileNo();
this.id = employee.getId();
}

public EmployeeDTO(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}

public String getFirstName() {
return firstName;
}

public void setFirstName(String firstName) {
this.firstName = firstName;
}

public String getLastName() {
return lastName;
}

public void setLastName(String lastName) {
this.lastName = lastName;
}

public String getUserName() {
return userName;
}

public void setUserName(String userName) {
this.userName = userName;
}

public String getEmailId() {
return emailId;
}

public void setEmailId(String emailId) {
this.emailId = emailId;
}

public String getEmpId() {
return empId;
}

public void setEmpId(String empId) {
this.empId = empId;
}

public String getBloodGp() {
return bloodGp;
}

public void setBloodGp(String bloodGp) {
this.bloodGp = bloodGp;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

public String getPersonalEmail() {
return personalEmail;
}

public void setPersonalEmail(String personalEmail) {
this.personalEmail = personalEmail;
}

public String getMobileNo() {
return mobileNo;
}

public void setMobileNo(String mobileNo) {
this.mobileNo = mobileNo;
}

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

}

--------------------------Employee----------------------
package com.ramsis.main.model;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table
public class Employee {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(name="first_name")
private String firstName;

@Column(name="last_name")
private String lastName;

@Column(name="user_name")
private String userName;

@Column(name="email_id")
private String emailId;

@Column(name="emp_id")
private String empId;

@Column(name="blood_gp")
private String bloodGp;

@Column(name="age")
private int age;

@Column(name="personal_email")
private String personalEmail;

@Column(name="mobile_no")
private String mobileNo;

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public String getFirstName() {
return firstName;
}

public void setFirstName(String firstName) {
this.firstName = firstName;
}

public String getLastName() {
return lastName;
}

public void setLastName(String lastName) {
this.lastName = lastName;
}

public String getUserName() {
return userName;
}

public void setUserName(String userName) {
this.userName = userName;
}

public String getEmailId() {
return emailId;
}

public void setEmailId(String emailId) {
this.emailId = emailId;
}

public String getEmpId() {
return empId;
}

public void setEmpId(String empId) {
this.empId = empId;
}

public String getBloodGp() {
return bloodGp;
}

public void setBloodGp(String bloodGp) {
this.bloodGp = bloodGp;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

public String getPersonalEmail() {
return personalEmail;
}

public void setPersonalEmail(String personalEmail) {
this.personalEmail = personalEmail;
}

public String getMobileNo() {
return mobileNo;
}

public void setMobileNo(String mobileNo) {
this.mobileNo = mobileNo;
}

}

--------------------------EmployeeServiceImpl-------------------------------

package com.ramsis.main.serviceImpl;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.ramsis.main.dto.EmployeeDTO;
import com.ramsis.main.model.Employee;
import com.ramsis.main.repository.EmployeeRepository;
import com.ramsis.main.service.EmployeeService;

@Service
public class EmployeeServiceImpl implements EmployeeService{

@Autowired
private EmployeeRepository employeeRepository;

public void createOrUpdateEmployee(EmployeeDTO empDto) {
Employee emp = convertDtoToModel(empDto);
employeeRepository.save(emp);
}

public List<EmployeeDTO> getAllEmployee() {
List<Employee> list = employeeRepository.findAll();
List<EmployeeDTO> employeeList = list.stream()
           .map(EmployeeDTO::new)
           .collect(Collectors.toCollection(ArrayList::new));
return employeeList;
}

public void deleteEmployee(Long id) {
employeeRepository.deleteById(id);
}

public EmployeeDTO editEmployee(Long id) {
Employee emp = employeeRepository.getOne(id);
return convertModelToDTO(emp);
}

private Employee convertDtoToModel(EmployeeDTO empDto) {
Employee emp = new Employee();
if (empDto.getId() != null) {
emp.setId(empDto.getId());
}
emp.setAge(empDto.getAge());
emp.setBloodGp(empDto.getBloodGp());
emp.setEmailId(empDto.getEmailId());
emp.setEmpId(empDto.getEmpId());
emp.setFirstName(empDto.getFirstName());
emp.setLastName(empDto.getLastName());
emp.setMobileNo(empDto.getMobileNo());
emp.setPersonalEmail(empDto.getPersonalEmail());
emp.setUserName(empDto.getUserName());
return emp;
}

private EmployeeDTO convertModelToDTO(Employee emp) {
EmployeeDTO empDTO = new EmployeeDTO();
empDTO.setId(emp.getId());
empDTO.setAge(emp.getAge());
empDTO.setBloodGp(emp.getBloodGp());
empDTO.setEmailId(emp.getEmailId());
empDTO.setEmpId(emp.getEmpId());
empDTO.setFirstName(emp.getFirstName());
empDTO.setLastName(emp.getLastName());
empDTO.setMobileNo(emp.getMobileNo());
empDTO.setPersonalEmail(emp.getPersonalEmail());
empDTO.setUserName(emp.getUserName());
return empDTO;
}
}

-----------------------------application.properties--------------------------

spring.mvc.view.prefix: /WEB-INF/jsp/
spring.mvc.view.suffix: .jsp
spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=cctns@123
spring.datasource.tomcat.max-wait=20000
spring.datasource.tomcat.max-active=50
spring.datasource.tomcat.max-idle=20
spring.datasource.tomcat.min-idle=15
spring.jpa.properties.hibernate.dialect org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.hibernate.ddl-auto = update

----------------------------pom.xml----------------------------------

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
   <version>2.1.0.RELEASE</version>  
</parent>

    <groupId>com.ramsis.main</groupId>
    <artifactId>EMSApp</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>
    <properties>
        <maven.compiler.target>1.8</maven.compiler.target>
        <maven.compiler.source>1.8</maven.compiler.source>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
        <dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
        <!-- JSP -->
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-jasper</artifactId>
        </dependency>
        <!-- jstl for jsp -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>





Javascript Ajax Code


Simple Ajax code
Ajax Call onkeyup Event in jsp


<!--  Simple Ajax code  -->

<script language="javascript" type="text/javascript">
 var xmlHttp;
 function modifyData(Str) {

   if(document.getElementById("td1").style.display=="none")
         {document.getElementById("td1").style.display="block";}
         else{document.getElementById("td1").style.display="none";}

  var versionIds = [ "Msxml2.XMLHTTP", "Microsoft.XMLHTTP",
    "Microsoft.XMLDOM", "Msxml2.XMLHTTP.6.0", "Msxml2.XMLHTTP.5.0",
    "Msxml2.XMLHTTP.4.0", "Msxml2.XMLHTTP.3.0",
    "Msxml2.XMLHTTP.2.6", "Microsoft.XMLHTTP.1.0",
    "Microsoft.XMLHTTP.1" ];
  if (typeof XMLHttpRequest != "undefined") {
   xmlHttp = new XMLHttpRequest();
  } else if (window.ActiveXObject) {
   for ( var i = 0; i < versionIds.length; i++) {
    try {

     return new ActiveXObject(versionIds[i]);

    } catch (e) {
     alert("Browser does not support XMLHTTP Request");
    }
   }
   new XDomainRequest();
  }
  if (xmlHttp == null) {
   alert("Browser does not support XMLHTTP Request");
   return;
  }

  var url = "outPut.jsp?";
  url += "id=" + Str;
  url += "&sid=" + Math.random();
  xmlHttp.onreadystatechange = stateChange;
  xmlHttp.open("POST", url, true);
  xmlHttp.send(null);

  function stateChange() {
   if (xmlHttp.readyState == 4 || xmlHttp.readyState == "complete") {
     document.getElementById("td1").innerHTML=xmlHttp.responseText;
   }
  }
 }

</script>

    <table align="center" >
       <tr>
            <td><input type="text" size="30" maxlength="50" name="search1" value=""/><input type="button"     name="searchbtn" value="Search" onclick='modifyData(this.value);'/></td>
       </tr>
       <tr>
            <td id="td1"></td>
       </tr>
    </table>

Saturday 24 June 2023

Exception Handling and Validation in Spring Boot

Handling exceptions is an important part of building a robust application. This tutorial I will illustrate how to implement Exception Handling and Validation with Spring for a REST API
please follow the steps and download the code below. 



-------------------------------- UserController -------------------------------------------

package com.ramsis.restApi.controller;

import java.util.List;
import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.ramsis.restApi.payloads.ApiResponse;
import com.ramsis.restApi.payloads.UserDto;
import com.ramsis.restApi.services.UserService;

@RestController
@RequestMapping("/api/users")
public class UserController {
@Autowired
private UserService userService;

@PostMapping("/")
public ResponseEntity<UserDto>addUser(@Valid @RequestBody UserDto userDto)
{
UserDto createUser=this.userService.createuser(userDto);
return new ResponseEntity<>(createUser,HttpStatus.CREATED);
}

@PutMapping("/{userId}")
public ResponseEntity<UserDto> updateUser(@Valid @RequestBody UserDto userDto , @PathVariable("userId")  Integer userId){

UserDto updateUser=this.userService.updateUser(userDto, userId);
return ResponseEntity.ok(updateUser);
}

@DeleteMapping("/{userId}")
public ResponseEntity<ApiResponse>deletedUser(@PathVariable("userId") Integer userId)
{
this.userService.deleteUser(userId);
return new ResponseEntity<ApiResponse>(new ApiResponse("user delete succsfully",String.valueOf(HttpStatus.OK.value()),true),HttpStatus.OK);

}
@GetMapping("/")
public ResponseEntity<List<UserDto>> getAllUsers()
{
return ResponseEntity.ok(this.userService.getAllUsers());
}

@GetMapping("/{userId}")
public ResponseEntity<UserDto> getAllUsers(@PathVariable("userId") Integer userId)
{
return ResponseEntity.ok(this.userService.getByuserId(userId));
}

}







--------------------------------- GlobelExceptionHandler --------------------------------

package com.ramsis.restApi.exception;

import java.util.HashMap;
import java.util.Map;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import com.ramsis.restApi.payloads.ApiResponse;

@RestControllerAdvice
public class GlobelExceptionHandler {
@ExceptionHandler(ResourcesNotoundExcpetion.class)
public ResponseEntity<ApiResponse> resourceNotfoiunexceptionHandler(ResourcesNotoundExcpetion ex)
{
String message=ex.getMessage();
ApiResponse apiResponse=new ApiResponse(message,String.valueOf(HttpStatus.NOT_FOUND.value()),false);
return new ResponseEntity<ApiResponse>(apiResponse,HttpStatus.NOT_FOUND);
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<Map<String, String>> handleMethodArgsNotValidateExpection(MethodArgumentNotValidException ex)
{
Map<String, String> resp=new HashMap<>();
ex.getBindingResult().getAllErrors().forEach(error->{
String filedName=((FieldError)error).getField();
String messsage=error.getDefaultMessage();
resp.put(filedName, messsage);
});
return new ResponseEntity<Map<String,String>>(resp,HttpStatus.BAD_REQUEST);
}
@ExceptionHandler(ApiException.class)
public ResponseEntity<ApiResponse> handleApiException(ApiException ex)
{
String message=ex.getMessage();
ApiResponse apiResponse=new ApiResponse(message,String.valueOf(HttpStatus.BAD_REQUEST.value()),true);
return new ResponseEntity<ApiResponse>(apiResponse,HttpStatus.BAD_REQUEST);
}

}

----------------------------------- ResourcesNotoundExcpetion -----------------------------------

package com.ramsis.restApi.exception;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class ResourcesNotoundExcpetion  extends RuntimeException{

String resourceName;
String feildName;
long feildValue;
public ResourcesNotoundExcpetion(String resourceName, String feildName, long feildValue) {
super(String.format("%s not found with his name %s :%s", resourceName,feildName,feildValue));
this.resourceName = resourceName;
this.feildName = feildName;
this.feildValue = feildValue;
}
}

------------------------ ApiResponse--------------------------

package com.ramsis.restApi.payloads;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class ApiResponse {
private String  meaassge;
private String  status;
private boolean success;

}

--------------------------- userDto --------------------------------

package com.ramsis.restApi.payloads;

import javax.validation.constraints.Email;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;

import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@NoArgsConstructor
@Setter
@Getter
public class UserDto {
private int userId;
@NotEmpty
@Size(min = 4,message ="username must be minimum 4 character")
private String name;
@Email(message = "your email id wrong")
private String email;
@NotEmpty
//@Size(min = 3,max = 15, message = "password must be minimum 3 and max 12 character")
@Pattern(regexp = "^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[!@#&()–[{}]:;',?/*~$^+=<>]).{5,15}$", message = ""
+ "Password must contain at least one digit [0-9]."
+ "Password must contain at least one lowercase Latin character [a-z]."
+ "Password must contain at least one uppercase Latin character [A-Z]."
+ "Password must contain at least one special character like ! @ # & ( )."
+ "Password must contain a length of at least 5 characters and a maximum of 15 characters.")
private String password;
private String region;
@Size(min = 2,message ="department must be minimum 2 character")
private String department;


}

--------------------------------- userRepository ------------------------------------

package com.ramsis.restApi.repository;

import java.util.Optional;

import org.springframework.data.jpa.repository.JpaRepository;

import com.ramsis.restApi.entities.User;

public interface UserRepository  extends JpaRepository<User, Integer>{
  Optional<User> findByEmail(String email);
}

---------------------------- userService --------------------------

package com.ramsis.restApi.services;

import java.util.List;

import com.ramsis.restApi.payloads.UserDto;

public interface UserService {
UserDto registerNewUser(UserDto user);
UserDto createuser(UserDto user);
UserDto updateUser(UserDto user, Integer userId);
UserDto getByuserId(Integer userId);
List<UserDto> getAllUsers();
void deleteUser(Integer userId);

}

--------------------------- userServiceImpl --------------------

package com.ramsis.restApi.services;

import java.util.List;
import java.util.stream.Collectors;
import org.modelmapper.ModelMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ramsis.restApi.entities.User;
import com.ramsis.restApi.exception.ResourcesNotoundExcpetion;
import com.ramsis.restApi.payloads.UserDto;
import com.ramsis.restApi.repository.UserRepository;

@Service
public class UserServiceImpl  implements UserService{
@Autowired
private ModelMapper mapper;
@Autowired
private UserRepository userRepository;

@Override
public UserDto createuser(UserDto userDto) {
User user=this.dtoToUser(userDto);
User saveUsers=this.userRepository.save(user);
return this.userToDto(saveUsers);
}

@Override
public UserDto updateUser(UserDto userDto, Integer userId) {
User user=this.userRepository.findById(userId)
.orElseThrow(()-> new ResourcesNotoundExcpetion("User","Id",userId));
user.setName(userDto.getName());
user.setEmail(userDto.getEmail());
user.setPassword(userDto.getPassword());
user.setRegion(userDto.getRegion());
user.setDepartment(userDto.getDepartment());
User updateUser=this.userRepository.save(user);
UserDto userDto1= this.userToDto(updateUser);
return userDto1;
}

@Override
public UserDto getByuserId(Integer userId) {
User user=this.userRepository.findById(userId)
.orElseThrow(()-> new ResourcesNotoundExcpetion("User","Id",userId));
return this.userToDto(user);
}

@Override
public List<UserDto> getAllUsers() {

List<User> users=this.userRepository.findAll();
List<UserDto> userDtos=users.stream().map(user->this.userToDto(user)).collect(Collectors.toList());
return userDtos;
}

@Override
public void deleteUser(Integer userId) {
User user =this.userRepository.findById(userId).orElseThrow(()-> new ResourcesNotoundExcpetion("User","Id",userId));
this.userRepository.delete(user);
}
@Override
public UserDto registerNewUser(UserDto userDto) {
User user = this.mapper.map(userDto, User.class);
//endoed the password
user.setPassword(user.getPassword());
User newUser=this.userRepository.save(user);
return this.mapper.map(newUser, UserDto.class);
}

private User dtoToUser(UserDto userDto)
{
User user=this.mapper.map(userDto, User.class);
// user.setUserId(userDto.getUserId());
// user.setName(userDto.getName());
// user.setEmail(userDto.getEmail());
// user.setDepartment(userDto.getDepartment());
// user.setPassword(userDto.getPassword());
// user.setRegion(userDto.getRegion());
return user;
}
public UserDto userToDto (User user)
{
UserDto userDto=this.mapper.map(user, UserDto.class);
// userDto.setUserId(user.getUserId());
// userDto.setName(user.getName());
// userDto.setEmail(user.getEmail());
// userDto.setDepartment(user.getDepartment());
// userDto.setPassword(user.getPassword());
// userDto.setRegion(user.getRegion());
return userDto;
}

}

------------------------- pom.xml ----------------------------

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.12</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.ramsis</groupId>
<artifactId>restApiWithValidationErrorHandling</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>restApiWithValidationErrorHandling</name>
<description>Demo project for Spring boot Exception Handling and validation</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.modelmapper/modelmapper -->
<dependency>
<groupId>org.modelmapper</groupId>
<artifactId>modelmapper</artifactId>
<version>3.1.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-validation -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>

</project>


------------------------------ application.properties -------------------------

spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=cctns@123

spring.jpa.show-sql = true
spring.jpa.properties.hibernate.format_sql=true

logging.level.org.hibernate.type.descriptor.sql=trace

spring.jpa.hibernate.ddl-auto = update
#spring.jpa.hibernate.ddl-auto = none





Wednesday 14 June 2023

SpringBoot Pagination With Bootstrap , jquery and Ajax

If we have a large dataset and we want to present it to the user in smaller chunks, pagination is helpful solution. So in the tutorial, I am showing how to create "SpringBoot Ajax Pagination Example" use JQuery Ajax and Bootstrap to build a table solution for pagination with SpringBoot RestAPIs examples.

The Spring Data JPA provides a useful Pageable interface that provides number of methods for building pagination in the UI side. Even you can sort the data returned by the JPA query using the methods provided in this interface API.





--------------------------------- pom.xml---------------------------------


<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.roytuts</groupId>
<artifactId>springboot-jpa-pageable</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.6</version>
</parent>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

---------------------------------- ProductRestController -------------------------------

package com.ramsis.springboot.jpa.pageable.rest.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import com.ramsis.springboot.jpa.pageable.dto.ProductDto;
import com.ramsis.springboot.jpa.pageable.service.ProductService;

@RestController
@CrossOrigin(origins = "*")
public class ProductRestController {

@Autowired
private ProductService productService;

@GetMapping("/product")
public Page<ProductDto> products(Pageable pageable) {
return productService.products(pageable);
}

}


-------------------------------- Product Entity------------------------

package com.ramsis.springboot.jpa.pageable.entity;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table
public class Product {

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;

@Column
private String name;

@Column
private String prise;

public Integer getId() {
return id;
}

public void setId(Integer id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getPrise() {
return prise;
}

public void setPrise(String prise) {
this.prise = prise;
}

}

---------------------------------- ProductDto ----------------------------

package com.ramsis.springboot.jpa.pageable.dto;

public class ProductDto {

private Integer id;
private String name;
private String prise;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPrise() {
return prise;
}
public void setPrise(String prise) {
this.prise = prise;
}

}

------------------------ EntityDtoConverter -------------------------

package com.ramsis.springboot.jpa.pageable.converter;


import com.ramsis.springboot.jpa.pageable.dto.ProductDto;

import com.ramsis.springboot.jpa.pageable.entity.Product;


public final class EntityDtoConverter {


private EntityDtoConverter() {

}


public static ProductDto entityToDto(Product product) {

ProductDto productDto = new ProductDto();


productDto.setId(product.getId());

productDto.setName(product.getName());

productDto.setPrise(product.getPrise());

return productDto;

}


public static Product dtoToEntity(ProductDto productDto) {

Product product = new Product();


product.setId(productDto.getId());

product.setName(productDto.getName());

product.setPrise(productDto.getPrise());

return product;

}


}

---------------------------- ProductService --------------------------

package com.ramsis.springboot.jpa.pageable.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;

import com.ramsis.springboot.jpa.pageable.converter.EntityDtoConverter;
import com.ramsis.springboot.jpa.pageable.dto.ProductDto;
import com.ramsis.springboot.jpa.pageable.entity.Product;
import com.ramsis.springboot.jpa.pageable.repository.ProductRepository;

@Service
public class ProductService {

@Autowired
private ProductRepository productRepository;

public Page<ProductDto> products(Pageable pageable) {
Page<Product> products = productRepository.findAll(pageable);

Page<ProductDto> pages = products.map(entity -> {
ProductDto dto = EntityDtoConverter.entityToDto(entity);
return dto;
});

return pages;
}

}


------------------------------- ProductRepository -----------------------------

package com.ramsis.springboot.jpa.pageable.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import com.ramsis.springboot.jpa.pageable.entity.Product;

public interface ProductRepository extends JpaRepository<Product, Integer> {

}

----------------------------------- table-pagination.js ---------------------------

$(document).ready(function() {

let totalPages = 1;

function fetchNotes(startPage) {

//console.log('startPage: ' +startPage);

/**

* get data from Backend's REST API

*/

$.ajax({

type : "GET",

url : "http://localhost:8080/product",

data: {

page: startPage,

size: 10

},

success: function(response){

$('#noteTable tbody').empty();

// add table rows

$.each(response.content, (i, product) => {

let noteRow = '<tr>' +

'<td>' + product.id + '</td>' +

'<td>' + product.name + '</td>' +

'<td>' + product.prise + '</td>' +

'</tr>';

$('#noteTable tbody').append(noteRow);

});

if ($('ul.pagination li').length - 2 != response.totalPages){

// build pagination list at the first time loading

$('ul.pagination').empty();

buildPagination(response);

}

},

error : function(e) {

alert("ERROR: ", e);

console.log("ERROR: ", e);

}

});

}

function buildPagination(response) {

totalPages = response.totalPages;

var pageNumber = response.pageable.pageNumber;

var numLinks = 10;

// print 'previous' link only if not on page one

var first = '';

var prev = '';

if (pageNumber > 0) {

if(pageNumber !== 0) {

first = '<li class="page-item"><a class="page-link">« First</a></li>';

}

prev = '<li class="page-item"><a class="page-link">‹ Prev</a></li>';

} else {

prev = ''; // on the page one, don't show 'previous' link

first = ''; // nor 'first page' link

}

// print 'next' link only if not on the last page

var next = '';

var last = '';

if (pageNumber < totalPages) {

if(pageNumber !== totalPages - 1) {

next = '<li class="page-item"><a class="page-link">Next ›</a></li>';

last = '<li class="page-item"><a class="page-link">Last »</a></li>';

}

} else {

next = ''; // on the last page, don't show 'next' link

last = ''; // nor 'last page' link

}

var start = pageNumber - (pageNumber % numLinks) + 1;

var end = start + numLinks - 1;

end = Math.min(totalPages, end);

var pagingLink = '';

for (var i = start; i <= end; i++) {

if (i == pageNumber + 1) {

pagingLink += '<li class="page-item active"><a class="page-link"> ' + i + ' </a></li>'; // no need to create a link to current page

} else {

pagingLink += '<li class="page-item"><a class="page-link"> ' + i + ' </a></li>';

}

}

// return the page navigation link

pagingLink = first + prev + pagingLink + next + last;

$("ul.pagination").append(pagingLink);

}

$(document).on("click", "ul.pagination li a", function() {

var data = $(this).attr('data');

let val = $(this).text();

console.log('val: ' + val);

// click on the NEXT tag

if(val.toUpperCase() === "« FIRST") {

let currentActive = $("li.active");

fetchNotes(0);

$("li.active").removeClass("active");

// add .active to next-pagination li

currentActive.next().addClass("active");

} else if(val.toUpperCase() === "LAST »") {

fetchNotes(totalPages - 1);

$("li.active").removeClass("active");

// add .active to next-pagination li

currentActive.next().addClass("active");

} else if(val.toUpperCase() === "NEXT ›") {

let activeValue = parseInt($("ul.pagination li.active").text());

if(activeValue < totalPages){

let currentActive = $("li.active");

startPage = activeValue;

fetchNotes(startPage);

// remove .active class for the old li tag

$("li.active").removeClass("active");

// add .active to next-pagination li

currentActive.next().addClass("active");

}

} else if(val.toUpperCase() === "‹ PREV") {

let activeValue = parseInt($("ul.pagination li.active").text());

if(activeValue > 1) {

// get the previous page

startPage = activeValue - 2;

fetchNotes(startPage);

let currentActive = $("li.active");

currentActive.removeClass("active");

// add .active to previous-pagination li

currentActive.prev().addClass("active");

}

} else {

startPage = parseInt(val - 1);

fetchNotes(startPage);

// add focus to the li tag

$("li.active").removeClass("active");

$(this).parent().addClass("active");

//$(this).addClass("active");

}

});

(function(){

// get first-page at initial time

fetchNotes(0);

})();

});


------------------------------------------------------ index.html -------------------------------------------

<!DOCTYPE html>

<html lang="en">

<head>

<title>Bootstrap Ajax SpringBoot Pagination</title>

<meta charset="utf-8">

<meta name="viewport" content="width=device-width, initial-scale=1">

<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.2/dist/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous">

</head>

<body>

<div class="container">

<div class="row">

<div class="col-sm-7" style="background-color: #f6e796; margin: 5px; padding: 5px; border-radius: 5px; margin: auto;">

<div class="alert alert-warning">

<h4>SpringBoot Pagination + Bootstrap + jquery + Ajax </h4>

</div>

<table id="noteTable" class="table table-hover table-sm">

<thead class="thead-dark">

<tr>

<th>Id</th>

<th>Name</th>

<th>Prise</th>

</tr>

</thead>

<tbody>

</tbody>

</table>

<ul class="pagination justify-content-center" style="margin:10px 0; cursor: pointer;">

</ul>

</div>

</div>

</div>

<script src="https://code.jquery.com/jquery-3.6.0.min.js" crossorigin="anonymous"></script>

<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/2.10.2/umd/popper.min.js" crossorigin="anonymous" referrerpolicy="no-referrer"></script>

<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.2/dist/js/bootstrap.min.js" crossorigin="anonymous"></script>

<script src="table-pagination.js"></script>

</body>

</html>






Friday 5 May 2023

How to mask and unmask input text in html






<html>
   <head>
      <style>

              .form-group{
                position: relative;
                width: 50%;
                overflow:hidden;
              }

              .form-group > .toggleMask{
                position: absolute;
                top: 0;
                right: -20px;
                text-indent: -30px;
                height:100%;
                line-height: 2;
                pointer-events: auto;
                z-index: 5;
                cursor: pointer;
              }
              .form-group > .toggleMask ~ input{
                padding-right: 30px;
              }
              .form-group > .toggleMask:checked::before{
                content:"\e105";
              }
       </style>

   <script src=https://stacksnippets.net/scripts/snippet-javascript-console.min.js?v=1 type="text/javascript"></script>
   <link href=https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css rel="stylesheet"/>        
  </head>

 <body>
  <div class="form-group">
    <input type='checkbox' class="glyphicon glyphicon-eye-close toggleMask"/>
    <input type="password" id="data" class="form-control" />
    <button onclick="chackdate()"> check </button>
  </div>

   <script>
       document.querySelector('.toggleMask').addEventListener('change', onToggleMaskChange);
        function onToggleMaskChange(){
          this.nextElementSibling.type = this.checked ? 'text' : 'password'
        }                    
                             
       function chackdate(){
          var data = document.getElementById("data").value;
           alert(data);
       }
   </script>    
  </body>
</html>
--------------------------------------------------------------------------------------------------

Mask and unmask text on onmouseover event in html


<html>

  <head>
   <script>
       function mouseover() {
          let obj = document.getElementById('myPassword');
              obj.type = 'text';
       }
       function mouseout() {
          let obj = document.getElementById('myPassword');
              obj.type = 'password';
       }
   </script>

<input type="password" name="password" id="myPassword" size="30" onmouseover="mouseover();" onmouseout="mouseout();" />

</body>
</html>



Thursday 30 March 2023

How to Implement Fuzzy Search or Approximate String Search in Java

What Is Fuzzy Matching ?

Fuzzy Matching or Approximate String Matching is among the most discussed issues in IT.
It is a method that offers an improved ability to identify two elements of text, strings, or entries that are approximately similar but are not precisely the same.
In other words, a fuzzy method may be applied when an exact match is not found for phrases or sentences on a database. Indeed, this method attempts to find a match greater than the application-defined match percentage threshold.

As below mentioned code you can implement Fuzzy Search. 


import org.apache.commons.codec.language.Soundex;
import org.apache.commons.text.similarity.LevenshteinDistance;
import me.xdrop.fuzzywuzzy.FuzzySearch;

public class FuzzySearch {

   public boolean searchFuzzy(String searchableName, String targetName) {
       boolean matches=false;
       int  fuzzySearchNameThreashold=70;
        System.out.println("FuzzySearch.tokenSetRatio(searchableName, targetName) >> "+FuzzySearch.tokenSetRatio(searchableName, targetName));
        System.out.println("levenshteinDistanceMatch(searchableName, targetName) >> "+levenshteinDistanceMatch(searchableName, targetName));
       if((soundexMatch(searchableName,targetName)
        || FuzzySearch.tokenSetRatio(searchableName, targetName) >= fuzzySearchNameThreashold
        || levenshteinDistanceMatch(searchableName, targetName) >= fuzzySearchNameThreashold)) {    
              matches = true;
        }
       return matches;
    }

   public boolean soundexMatch(String searchableName, String targetName) {
         Soundex soundex = new Soundex();
         return soundex.soundex(searchableName).equals(soundex.soundex(targetName));
   }

     public double levenshteinDistanceMatch(String searchableName, String targetName) {
         LevenshteinDistance levenshteinDistance = new LevenshteinDistance();
         return (100 - Math.round((((double) levenshteinDistance.apply(searchableName, targetName) / searchableName.length()) * 100)));
       }

 public static void main(String aa[]) {
       FuzzySearchUtil fuzzySearchUtil = new FuzzySearchUtil();
       boolean flag = fuzzySearchUtil.searchFuzzy("Amn","Aman Kumar");
       System.out.println("flag >> "+flag);
 }

    }
}

----------------------- Maven Dependency -------------------------

           <dependency>
                <groupId>me.xdrop</groupId>
                <artifactId>fuzzywuzzy</artifactId>
                <version>1.3.1</version>
             </dependency>

        <!-- https://mvnrepository.com/artifact/commons-codec/commons-codec -->
             <dependency>
                 <groupId>commons-codec</groupId>
                 <artifactId>commons-codec</artifactId>
                 <version>1.10</version>
             </dependency>

           <dependency>
              <groupId>org.apache.commons</groupId>
              <artifactId>commons-text</artifactId>
              <version>1.3</version>
           </dependency>