Saturday 14 September 2024

How to build spring boot application for Production , UAT Testing and Dev Environment using Eclipse.

In this blog I will show you that how we can build Spring Boot Application for different Environments like Production , UAT Testing and Dev. Here i am creating a Spring Boot Application and also you can download this application at the end of this post.

create a Controller class
------------------------------ AppController.class -----------------------------

@RestController
@RequestMapping("/api")
public class AppController {

@GetMapping("/status")
public String runningStatus() {
String deployName = VariableConfig.CURRENT_ENVIRONMENT;
System.out.println("/api/=> runningStatus()..."+VariableConfig.CURRENT_ENVIRONMENT);
return "<h3 style="color: red;">"+deployName+"</h3><h2 style="color: green;">Application is Running....</h2>";
}

}


create a config class for set currrent Environment variable

--------------------------------VariableConfig.class---------------------

@Component
public class VariableConfig {

public VariableConfig() {

}

public static String CURRENT_ENVIRONMENT;

@Value("${environment.current}")
public void setCurrentEnvironment(String currentEnvironment) {
CURRENT_ENVIRONMENT = currentEnvironment;
}
}


create four properties files for local, prod and stage Environments.

application.properties
application-local.properties
application-prod.properties
application-stage.properties

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

spring.profiles.active=@activatedProperties@

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

profile.environment.current=local
server.servlet.context-path=/profile
server.port=8080
environment.current=Local Environment


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

profile.environment.current=prod
server.servlet.context-path=/profile
server.port=9090
environment.current=Prod Environment

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

profile.environment.current=stage
server.servlet.context-path=/profile
server.port=2020
environment.current=Stage Environment


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


<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0" 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.6</version>
<relativepath> <!--lookup parent from repository-->
</relativepath></parent>
<groupid>com.ramsis</groupid>
<artifactid>profile</artifactid>
<version>0.0.1-SNAPSHOT</version>
<name>profile</name>
<description>Demo project for Spring Boot</description>
<properties>
<java .version="">11</java>
</properties>
<dependencies>
<dependency>
<groupid>org.springframework.boot</groupid>
<artifactid>spring-boot-starter-web</artifactid>
</dependency>

<!--https://mvnrepository.com/artifact/commons-codec/commons-codec-->
<dependency>
<groupid>org.apache.commons</groupid>
<artifactid>commons-text</artifactid>
<version>1.3</version>
</dependency>


<dependency>
<groupid>org.springframework.boot</groupid>
<artifactid>spring-boot-starter-test</artifactid>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupid>org.springframework.boot</groupid>
<artifactid>spring-boot-maven-plugin</artifactid>
</plugin>
</plugins>
</build>

<profiles>
<profile>
<id>local</id>
<activation>
<activebydefault>true</activebydefault>
</activation>
<properties>
<activatedproperties>local</activatedproperties>
</properties>
</profile>

<!--Stage Environment-->
<profile>
<id>stage</id>
<properties>
<activatedproperties>stage</activatedproperties>
</properties>
</profile>

<!--Production Environment-->
<profile>
<id>prod</id>
<properties>
<activatedproperties>prod</activatedproperties>
</properties>
</profile>

</profiles>

</project>

------------------------------------------------------

Download Code

Monday 17 July 2023

Create CRUD App in ReactJS with Spring Boot RestAPI pagination, searching and sorting features.

In this tutorial, we are going to build a React CRUD Application with Pagination, Searching and sorting features also consume Restful APIs developed in Spring boot.

We will do all Searching, sorting and pagination implementation by Spring Boot Restful API.

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 @material-ui/core
npm install @material-ui/lab



























------------------------------package.json----------------------------------

{
  "name": "react-pagination-sorting-searching-app",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@fortawesome/fontawesome-free": "^6.4.0",
    "@material-ui/core": "^4.12.4",
    "@material-ui/lab": "^4.0.0-alpha.61",
    "@testing-library/jest-dom": "^5.16.5",
    "@testing-library/react": "^13.4.0",
    "@testing-library/user-event": "^13.5.0",
    "axios": "^1.4.0",
    "bootstrap": "^5.3.0",
    "react": "^18.2.0",
    "react-bootstrap": "^2.8.0",
    "react-dom": "^18.2.0",
    "react-router-dom": "^6.14.1",
    "react-scripts": "5.0.1",
    "react-table": "^7.8.0",
    "web-vitals": "^2.1.4"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": [
      "react-app",
      "react-app/jest"
    ]
  },
  "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 from "react";
import { Routes, Route ,Link} from "react-router-dom";
import "bootstrap/dist/css/bootstrap.min.css";
import "@fortawesome/fontawesome-free/css/all.css";
import "@fortawesome/fontawesome-free/js/all.js";
import "./App.css";
import Portfolio from "./components/Portfolio";
import AddPortfolio from "./components/AddPortfolio";
import EditPortfolio from "./components/EditPortfolio";


function App() {
  return (
    <div>
      <nav className="navbar navbar-expand navbar-dark bg-dark">
        <a href="/" className="navbar-brand">
          ramsis-code
        </a>
        <div className="navbar-nav mr-auto">
          <li className="nav-item">
            <Link to={"/"} className="nav-link">
             Fetch Portfolio
            </Link>
          </li>
          <li className="nav-item">
          <Link to={"/insert"} className="nav-link">
              Insert
          </Link>
          </li>
         
        </div>
      </nav>

      <div className="container mt-3">

       <Routes>
         <Route path='/' element={<Portfolio/>}></Route>
         <Route path='/insert' element={<AddPortfolio/>}></Route>
         <Route path='/edit/:id' element={<EditPortfolio/>}></Route>
         
       </Routes>
       
      </div>
    </div>
  );
}

export default App;



-------------------------------------AddPortfolio.js-------------------------------------

import React, { useState } from "react";
import PortfolioDataService from "../services/PortfolioService";

const AddPortfolio = () =>{

    const initialPortfolioState = {
        id: null,
        name: "",
        email: "",
        department: "",
        phone: "",
        city: ""
      };

      const [portfolio, setPortfolio] = useState(initialPortfolioState);
      const [submitted, setSubmitted] = useState(false);

        const [msgName, setMsgName] = useState("");
        const [msgEmail, setMsgEmail] = useState("");
        const [msgDept, setMsgDept] = useState("");
        const [msgCity, setMsgCity] = useState("");
        const [msgPhone, setMsgPhone] = useState("");
        const regexEmail = /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/i;
      const handleInputChange = event => {
        const { name, value } = event.target;
        setPortfolio({ ...portfolio, [name]: value });
      };

      const savePortfolio = () => {
        var data = {
          id: portfolio.id,
          name: portfolio.name,
          email: portfolio.email,
          department: portfolio.department,
          phone: portfolio.phone,
          city: portfolio.city
        };

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

          if(data.email===''){
            setMsgEmail("Email is Required.");
            return false;
          }else if(!regexEmail.test(data.email)){
            setMsgEmail("Invalid Email.");
            return false;
          } else{
            setMsgEmail('');
          }
         
          if(data.department===''){
            setMsgDept("Department is Required.");
            return false;
          }else{
            setMsgDept('');
          }

          if(data.phone===''){
            setMsgPhone("Phone is Required.");
            return false;
          }else{
            setMsgPhone('');
          }

          if(data.city===''){
            setMsgCity("City is Required.");
            return false;
          }else{
            setMsgCity('');
          }
     
     


        PortfolioDataService.create(data)
      .then(response => {
        setPortfolio({
          id: response.data.id,
          name: response.data.name,
          email: response.data.email,
          department: response.data.department,
          phone: response.data.phone,
          city: response.data.city
        });
        setSubmitted(true);
        console.log(response.data);
      })
      .catch(e => {
        console.log(e);
      });
  };

  const newPortfolio = () => {
    setPortfolio(initialPortfolioState);
    setSubmitted(false);
  };
  return (
  <div className="submit-form">

   {submitted ? (
        <div>
          <h5>You submitted successfully!</h5>
          <button className="btn btn-success" onClick={newPortfolio}>
            Add
          </button>
        </div>
      ) : (
        <div>
            <div className="form-group">
            <label htmlFor="name">Name</label>
            <input
              type="text"
              className="form-control"
              id="name"
              required
              value={portfolio.name}
              onChange={handleInputChange}
              name="name"
            />
            {msgName && <p style={{color: "red"}}>{msgName}</p>}
          </div>

          <div className="form-group">
            <label htmlFor="email">email</label>
            <input
              type="text"
              className="form-control"
              id="email"
              required
              value={portfolio.email}
              onChange={handleInputChange}
              name="email"
            />
              {msgEmail && <p style={{color: "red"}}>{msgEmail}</p>}
          </div>

          <div className="form-group">
            <label htmlFor="department">department</label>
            <input
              type="text"
              className="form-control"
              id="department"
              required
              value={portfolio.department}
              onChange={handleInputChange}
              name="department"
            />
             {msgDept && <p style={{color: "red"}}>{msgDept}</p>}
          </div>

          <div className="form-group">
            <label htmlFor="phone">phone</label>
            <input
              type="text"
              className="form-control"
              id="phone"
              required
              value={portfolio.phone}
              onChange={handleInputChange}
              name="phone"
            />
              {msgPhone && <p style={{color: "red"}}>{msgPhone}</p>}
          </div>

          <div className="form-group">
            <label htmlFor="city">city</label>
            <input
              type="text"
              className="form-control"
              id="city"
              required
              value={portfolio.city}
              onChange={handleInputChange}
              name="city"
            />
            {msgCity && <p style={{color: "red"}}>{msgCity}</p>}
          </div>
          <br/>
          <button onClick={savePortfolio} className="btn btn-success">
            Submit
          </button>  
        </div>
    )}
  </div>
   );

}

export default AddPortfolio

----------------------------------EditPortfolio.js ----------------------------------

import React, { useState, useEffect } from "react";
import PortfolioDataService from "../services/PortfolioService";
import { useParams } from "react-router-dom";

const  EditPortfolio = () =>{
    const {id} = useParams();
    const initialPortfolioState = {
        id: null,
        name: "",
        email: "",
        department: "",
        phone: "",
        city: ""
      };

      const [portfolio, setPortfolio] = useState(initialPortfolioState);
      const [submitted, setUpdate] = useState(false);
      const [message, setMessage] = useState("");

     const handleInputChange = event => {
        const { name, value } = event.target;
        setPortfolio({ ...portfolio, [name]: value });
      };

      const updatePortfolio = () => {
        var data = {
          id: portfolio.id,
          name: portfolio.name,
          email: portfolio.email,
          department: portfolio.department,
          phone: portfolio.phone,
          city: portfolio.city
        };

        PortfolioDataService.update(data.id,data)
      .then(response => {
        setMessage("The tutorial was updated successfully!");
        setUpdate(true);
        console.log(response.data);
      })
      .catch(e => {
        console.log(e);
      });
  };




  const newPortfolio = () => {
    setPortfolio(initialPortfolioState);
    setUpdate(false);
  };

      const getPortfolio = (id) => {
        PortfolioDataService.get(id)
          .then(response => {
            setMessage("The tutorial was updated successfully!");
            setPortfolio(response.data);
            console.log(response.data);
          })
          .catch(e => {
            console.log(e);
          });
      };

      useEffect(() => {
        getPortfolio(id);
      }, [id]);
   

      return (
        <div className="submit-form">
     
         {submitted ? (
              <div>
                <h6>Your form is updated successfully!</h6>
                <button className="btn btn-success" onClick={newPortfolio}>
                  Add
                </button>
              </div>
            ) : (
              <div>
                  <div className="form-group">
                  <label htmlFor="name">Name</label>
                  <input
                    type="text"
                    className="form-control"
                    id="name"
                    required
                    value={portfolio.name}
                    onChange={handleInputChange}
                    name="name"
                  />
                </div>
     
                <div className="form-group">
                  <label htmlFor="email">email</label>
                  <input
                    type="text"
                    className="form-control"
                    id="email"
                    required
                    value={portfolio.email}
                    onChange={handleInputChange}
                    name="email"
                  />
                </div>
     
                <div className="form-group">
                  <label htmlFor="department">department</label>
                  <input
                    type="text"
                    className="form-control"
                    id="department"
                    required
                    value={portfolio.department}
                    onChange={handleInputChange}
                    name="department"
                  />
                </div>
     
                <div className="form-group">
                  <label htmlFor="phone">phone</label>
                  <input
                    type="text"
                    className="form-control"
                    id="phone"
                    required
                    value={portfolio.phone}
                    onChange={handleInputChange}
                    name="phone"
                  />
                </div>
     
                <div className="form-group">
                  <label htmlFor="city">city</label>
                  <input
                    type="text"
                    className="form-control"
                    id="city"
                    required
                    value={portfolio.city}
                    onChange={handleInputChange}
                    name="city"
                  />
                </div>
                <br/>
                <button onClick={updatePortfolio} className="btn btn-success">
                  Update
                </button>  
              </div>
          )}
        </div>
         );

}

export default EditPortfolio;


---------------------------Portfolio.js--------------------------

import React, { useState, useEffect } from "react";
import Table from 'react-bootstrap/Table';
import Pagination from "@material-ui/lab/Pagination";
import PortfolioDataService from "../services/PortfolioService";
import {Link } from "react-router-dom";

const Portfolio = (props) => {
  const [portfolios, setPortfolios] = useState([]);
  const [searchName, setSearchName] = useState("");
  const [page, setPage] = useState(1);
  const [count, setCount] = useState(0);
  const [pageSize, setPageSize] = useState(5);
  const [pageSort, setPageSort] = useState("id,asc");
  const pageSizes = [5, 8, 10];
  const retrievePortfolios = () => {
        const params = getRequestParams(searchName, page, pageSize, pageSort);
        PortfolioDataService.getAll(params)
          .then((response) => {
            const { portfolios, totalPages } = response.data;
            console.log(portfolios);
            setPortfolios(portfolios);
            setCount(totalPages);
            console.log("param"+params);
            console.log(response.data);
          })
          .catch((e) => {
            console.log(e);
          });
      };

      const getPortfolio = () => {
        retrievePortfolios();
      }

      useEffect(retrievePortfolios, [page, pageSize,pageSort,searchName]);
     
      const handlePageChange = (event, value) => {
        setPage(value);
      };

     
      const deleteTutorial = (rowIndex) => {
   
        PortfolioDataService.remove(rowIndex)
          .then((response) => {
            getPortfolio();
          })
          .catch((e) => {
            console.log(e);
          });
      };
   
      const handlePageSizeChange = (event) => {
        setPageSize(event.target.value);
        setPage(1);
      };

   const handlePageSortClick = (event,value) =>{
        //alert(event.currentTarget.id);
        const sdir="asc";
        const sortString = event.currentTarget.id+","+sdir;
        setPageSort(sortString);
        retrievePortfolios();
     }
     
      const findByName = () => {
        setPage(1);
        retrievePortfolios();
      };

      const onChangeSearchName = (e) => {
        const searchName = e.target.value;
        setSearchName(searchName);
      };
   
      const getRequestParams = (searchName, page, pageSize, pageSort) => {
        let params = {};
   
        if (searchName) {
          params["name"] = searchName;
        }
   
        if (page) {
          params["page"] = page - 1;
        }
   
        if (pageSize) {
          params["size"] = pageSize;
        }

        if (pageSort) {
            params["sort"] = pageSort;
          }
   
        return params;
      };
      return (
   
   <>
      <div className="list row">
      <div className="col-md-8">
        <div className="input-group mb-3">
          <input
            type="text"
            className="form-control"
            placeholder="Search by title"
            value={searchName}
            onChange={onChangeSearchName}
          />
          <div className="input-group-append">
            <button
              className="btn btn-outline-secondary"
              type="button"
              onClick={findByName}
            >
              Search
            </button>
          </div>
        </div>
      </div>
      <div className="col-md-12 list">
      <div className="mt-6 my-3">
          {"Rows per page: "}
          <select onChange={handlePageSizeChange} value={pageSize}>
            {pageSizes.map((size) => (
              <option key={size} value={size}>
                {size}
              </option>
            ))}
          </select>
       </div>

 <div className="col-md-12 list">  
  <Table table table-striped table-bordered>
        <thead>
           <th><Link id="id" onClick={handlePageSortClick}>Id</Link></th>
           <th><Link id="name" onClick={handlePageSortClick}>Name</Link></th>
           <th><Link id="email" onClick={handlePageSortClick}>Email</Link></th>
           <th><Link id="department" onClick={handlePageSortClick}>Department</Link></th>
           <th><Link id="phone" onClick={handlePageSortClick}>Phone</Link></th>
           <th><Link id="city" onClick={handlePageSortClick}>City</Link></th>
           <th><Link>Action</Link></th>
        </thead>
        <tbody>

            {
            portfolios.map((data,index)=>(
           
            <tr>  
                <td>{data.id}</td>
                <td>{data.name}</td>                
                <td>{data.email}</td>
                <td>{data.department}</td>
                <td>{data.phone}</td>
                <td>{data.city}</td>
                <td>
             
             
                <Link to={"/edit/"+data.id} >
                 <i className="far fa-edit action fa-1x mx-2" style={{color: "green"}}></i>
                </Link>
               
               
                <span  onClick={() => deleteTutorial(data.id)}>
                  <i className="fas fa-trash action fa-1x mx-2"  style={{color: "red"}}></i>
                </span>
                </td>
            </tr>
                ))
            }
       </tbody>
       </Table>
       </div>
      </div>
      <div className="mt-6">
       <Pagination
            className="my-2"
            count={count}
            page={page}
            siblingCount={1}
            boundaryCount={1}
            variant="outlined"
            shape="rounded"
            onChange={handlePageChange}
          />
         </div>
         </div>  
    </>
   
    );
};

export default Portfolio;


-------------------------PortfolioService.js-------------------------

import http from "../http-common";

const getAll = (params) => {
    return http.get("/portfolios", { params });
  };

const create = (data) => {
  return http.post("/portfolios", data);
};  

const remove = (id) => {
  return http.delete(`/portfolios/${id}`);
};

const get = (id) => {
  return http.get(`/portfolios/${id}`);
};

const update = (id, data) => {
  return http.put(`/portfolios/${id}`, data);
};

  const PortfolioService = {
    getAll,
    create,
    remove,
    get,
    update,
  };
 
  export default PortfolioService;

-----------------------------http-common.js----------------------------

import axios from "axios";

export default axios.create({
  baseURL: "http://localhost:9090/api",
  headers: {
    "Content-type": "application/json"
  }
});