Thursday, January 9, 2025

How to configure CI/CD using Jenkin, GITHub and NET core API in the local laptop.

Step1: First install Jenkin and Git in your laptop

Step2: Install plugin such as git, pipeline, MSBuild

Step3: Create a .net core api project and push code in the github. note: your github project must have read and write permission. Otherwise, Jenkin won’t be able to build the code.

Step4: Do some setup in jenkin such as go to Dashboard>Manage Jenkins>Tools and then add this

Git installations >> C:\Program Files\Git\cmd\git.exe (set git.exe path after git installations)

 

Step5: Open Jenkin and go to new item > enter name and then select pipeline.  And then in the pipeline past this script

 

Without approver:


pipeline {

    agent any

    stages {

        stage('Clone') {

            steps {

                git credentialsId: 'deb-credentials', url: 'https://github.com/debobrotadas/itemdetails.git'

            }

        }

        stage('Restore Dependencies') {

            steps {

                bat 'dotnet restore'

            }

        }

        stage('Build') {

            steps {

                bat 'dotnet build'

            }

        }

        stage('Publish') {

            steps {

                bat 'dotnet publish -c Release -o output'

            }

        }

        stage('Deploy to IIS') {

            steps {

                bat '''

                xcopy /s /e /y output "C:\\inetpub\\wwwroot\\itemdetails"

                iisreset

                '''

            }

        }

    }

}

 

With approver and email send:

pipeline {

    agent any

    environment {

        DEPLOY_DIR = 'C:\\inetpub\\wwwroot\\itemdetails'

        OUTPUT_DIR = 'C:\\inetpub\\wwwroot\\itemdetailspublish'

    }

    triggers {

        // Automatically build when changes are pushed to the repository

        githubPush()

    }

    stages {

        stage('Clone') {

            steps {

                git credentialsId: 'deb-credentials', url: 'https://github.com/debobrotadas/itemdetails.git'

            }

        }

        stage('Restore Dependencies') {

            steps {

                bat 'dotnet restore'

            }

        }

        stage('Build') {

            steps {

                bat 'dotnet build'

            }

        }

        stage('Publish') {

            steps {

                bat "dotnet publish -c Release -o ${OUTPUT_DIR}"

            }

        }

        stage('Approval') {

            steps {

                // Wait for manual approval before deploying

                input message: "Do you approve deployment to IIS?", ok: "Yes, Deploy"

            }

        }

        stage('Deploy to IIS') {

            steps {

                bat """

                xcopy /s /e /y ${OUTPUT_DIR} "${DEPLOY_DIR}"

                iisreset

                """

            }

        }

    }

    post {

        always {

            echo 'Cleaning up workspace...'

            cleanWs() // Clean up the workspace after build

        }

        success {

            script {

                echo 'Build and deployment succeeded!'

            }

            emailext subject: "SUCCESS: Job '${env.JOB_NAME}'",

                     body: """Build and deployment for '${env.JOB_NAME}' succeeded.

                              Deployed to: ${DEPLOY_DIR}""",

                     to: 'debobrotadas@gmail.com'

        }

        failure {

            script {

                echo 'Build or deployment failed!'

            }

            emailext subject: "FAILURE: Job '${env.JOB_NAME}'",

                     body: """The build or deployment for '${env.JOB_NAME}' has failed.

                              Please check the Jenkins logs for more details.""",

                     to: 'debobrotadas@gmail.com'

        }

    }

}

 

If you setup approver, then see this point to approve.

 

 

Here credentials 'deb-credentials’ need to setup in Jenkin.  This credential is Github credentials.

User name: debobrotadas

Password: Github personal access token

After build file will be located: C:\\inetpub\\wwwroot\\itemdetails

 

 

 

 

Step6: once all configuration is done, then click build now. Then you can see the stage. Each step will be successful.

 

 

 

 

 

Saturday, December 28, 2024

how to Insert data into header and line table with PK/FK relation at a time using .NET core API, Oracle21c (XE), postman

Project Structure:

Project Depedency:



Database Table: 

CREATE TABLE ItemHeader (
    HeaderId NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    HeaderName VARCHAR2(100),
    CreatedDate DATE DEFAULT SYSDATE
);

CREATE TABLE ItemLine (
    LineId NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, -- Auto-increment
    HeaderId NUMBER,
    LineDescription VARCHAR2(200),
    Quantity NUMBER,
    FOREIGN KEY (HeaderId) REFERENCES ItemHeader(HeaderId)
);

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace itemdetails.Models
{
    public class ItemHeader
    {
        [Key]
        public int HEADERID { get; set; } // Primary Key
        public string HEADERNAME { get; set; }
        public DateTime CREATEDDATE { get; set; }
        public ICollection<ItemLine> ItemLines { get; set; } // Navigation property
    }
}

using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Text.Json.Serialization;

namespace itemdetails.Models
{
    public class ItemLine
    {
        [Key]
        public int LINEID { get; set; } // Primary Key
        public int HEADERID { get; set; } // Foreign Key

        public string LINEDESCRIPTION { get; set; }
        public int QUANTITY { get; set; }

        [JsonIgnore] // Ignore during JSON deserialization
        public ItemHeader ItemHeader { get; set; } // Navigation property
    }
}

namespace itemdetails.Models
{
    public class ItemHeaderDto
    {
        public string HeaderName { get; set; }
        public DateTime CreatedDate { get; set; }
        public List<ItemLineDto> ItemLines { get; set; }
    }
}

namespace itemdetails.Models
{
    public class ItemLineDto
    {
        public string LineDescription { get; set; }
        public int Quantity { get; set; }
    }
}

using itemdetails.Models;

namespace itemdetails.Service
{
    public interface IItemService
    {
        Task<ItemHeader> CreateItemHeaderAsync(ItemHeader itemHeader);
        Task<ItemLine> CreateItemLineAsync(ItemLine itemLine);
        Task<ItemHeader> CreateItemHeaderWithLinesAsync (ItemHeader itemHeader);
    }
}

using itemdetails.Models;
using itemdetails.Repository;

namespace itemdetails.Service
{
    public class ItemService : IItemService
    {
        private readonly IItemRepository _itemRepository;
        public ItemService(IItemRepository itemRepository)
        {
            _itemRepository = itemRepository;
        }
        public Task<ItemHeader> CreateItemHeaderAsync(ItemHeader itemHeader)
        {
            throw new NotImplementedException();
        }      

        public Task<ItemLine> CreateItemLineAsync(ItemLine itemLine)
        {
            throw new NotImplementedException();
        }
        public Task<ItemHeader> CreateItemHeaderWithLinesAsync(ItemHeader itemHeader)
        {
            return _itemRepository.AddItemHeaderWithLinesAsync(itemHeader);
        }
    }
}

using itemdetails.Models;

namespace itemdetails.Repository
{
    public interface IItemRepository
    {
        Task<ItemHeader> AddItemHeaderAsync(ItemHeader itemHeader);    
        Task<ItemLine> AddItemLineAsync(ItemLine itemLine);
        Task<ItemHeader> AddItemHeaderWithLinesAsync(ItemHeader itemHeader);
    }
}

using itemdetails.Context;
using itemdetails.Models;
using Microsoft.EntityFrameworkCore;

namespace itemdetails.Repository
{
    public class ItemRepository : IItemRepository
    {
        private readonly ApplicationDbContext _dbContext;
        public ItemRepository(ApplicationDbContext dbContext) {
            _dbContext = dbContext;
        }
        public async Task<ItemHeader> AddItemHeaderAsync(ItemHeader itemHeader)
        {
            _dbContext.ItemHeaders.Add(itemHeader);
            await _dbContext.SaveChangesAsync();
            return itemHeader;
        }

        public async Task<ItemHeader> AddItemHeaderWithLinesAsync(ItemHeader itemHeader)
        {
            // Add the ItemHeader and line to the database
            _dbContext.ItemHeaders.Add(itemHeader);
            await _dbContext.SaveChangesAsync();
            return itemHeader;
        }

        public async Task<ItemLine> AddItemLineAsync(ItemLine itemLine)
        {
            _dbContext.ItemLines.Add(itemLine);
            await _dbContext.SaveChangesAsync();
            return itemLine;
        }
    }
}

using itemdetails.Models;
using itemdetails.Service;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;

namespace itemdetails.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class ItemController : ControllerBase
    {
        private readonly IItemService _itemService;
        public ItemController(IItemService itemService) {
            _itemService = itemService;
        }

        [HttpPost("Create")]
        public async Task<IActionResult> CreateItemHeaderWithLines([FromBody] ItemHeaderDto itemHeaderDto)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }

            // Map DTO to domain models
            var itemHeader = new ItemHeader
            {
                HEADERNAME = itemHeaderDto.HeaderName,
                CREATEDDATE = itemHeaderDto.CreatedDate,
                ItemLines = itemHeaderDto.ItemLines.Select(line => new ItemLine
                {
                    LINEDESCRIPTION = line.LineDescription,
                    QUANTITY = line.Quantity
                }).ToList()
            };

            var createdHeader = await _itemService.CreateItemHeaderWithLinesAsync(itemHeader);
            return CreatedAtAction(nameof(CreateItemHeaderWithLines), new { id = createdHeader.HEADERID }, createdHeader);
        }

    }
}

using itemdetails.Models;
using Microsoft.EntityFrameworkCore;

namespace itemdetails.Context
{
    public class ApplicationDbContext: DbContext
    {
        public DbSet<ItemHeader> ItemHeaders { get; set; }
        public DbSet<ItemLine> ItemLines { get; set; }
        public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            // ItemHeader configuration
            modelBuilder.Entity<ItemHeader>(entity =>
            {
                entity.ToTable("ITEMHEADER"); // Matches table name in database
                entity.HasKey(e => e.HEADERID);
            });

            // ItemLine configuration
            modelBuilder.Entity<ItemLine>(entity =>
            {
                entity.ToTable("ITEMLINE"); // Matches table name in database
                entity.HasKey(e => e.LINEID);

                // Foreign Key Relationship
                entity.HasOne(e => e.ItemHeader)
                      .WithMany(h => h.ItemLines)
                      .HasForeignKey(e => e.HEADERID)
                      .OnDelete(DeleteBehavior.Cascade);
            });
        }
    }
}

Program.cs:
using itemdetails.Context;
using Microsoft.Extensions.Options;
using System.Configuration;
using Oracle.ManagedDataAccess.Client;
using itemdetails.Service;
using itemdetails.Repository;
using Microsoft.EntityFrameworkCore;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
builder.Services.AddDbContext<ApplicationDbContext>(options =>
        options.UseOracle(builder.Configuration.GetConnectionString("OracleConnection")));
//builder.Services.AddDbContext<ApplicationDbContext>(options=> options.oracle(Configuration.GetConnectionString("DefaultConnection")));
builder.Services.AddScoped<IItemService, ItemService>();
builder.Services.AddScoped<IItemRepository, ItemRepository>();


// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseAuthorization();

app.MapControllers();

app.Run();

appsettings.json
{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "OracleConnection": "User Id=c##ekycdev;Password=ekycdev;Data Source=DESKTOP-8OFULSU:1521/XE"
  }
}

Postman:
{
  "HeaderName": "Header 1",
  "CreatedDate": "2024-12-27T00:00:00",
  "ItemLines": [
    {
      "LineDescription": "Item Line 1",
      "Quantity": 10
    },
    {
      "LineDescription": "Item Line 2",
      "Quantity": 5
    }
  ]
}



Output:
{
    "headerid": 6,
    "headername": "Header 1",
    "createddate": "2024-12-27T00:00:00",
    "itemLines": [
        {
            "lineid": 13,
            "headerid": 6,
            "linedescription": "Item Line 1",
            "quantity": 10
        },
        {
            "lineid": 14,
            "headerid": 6,
            "linedescription": "Item Line 2",
            "quantity": 5
        }
    ]
}

Monday, July 8, 2024

SPRING BOOT WITH JPA, HIBERNATE AND KAFKA

package com.abg.uniapi.entity;


import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;

@Entity
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private double price;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
}


package com.abg.uniapi.repository;

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

import com.abg.uniapi.entity.Product;

public interface ProductRepository extends JpaRepository<Product, Long> {

}

package com.abg.uniapi.service;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.abg.uniapi.entity.Product;

import com.abg.uniapi.repository.ProductRepository;

@Service
public class ProductService {
@Autowired
private ProductRepository productRepository;
public List<Product> getAllProducts(){
return productRepository.findAll();
}
public Product getProductById(Long id) {
return productRepository.findById(id).orElse(null);
}
public Product saveProudct(Product product) {
return productRepository.save(product);
}
public void deleteProduct(Long id) {
productRepository.deleteById(id);
}
}


package com.abg.uniapi.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
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.abg.uniapi.entity.Product;
import com.abg.uniapi.kafka.KafkaProducerService;
import com.abg.uniapi.service.ProductService;

@RestController
@RequestMapping("/api/product")
public class ProductController {
@Autowired
ProductService productService;
@Autowired
private KafkaProducerService kafkaProducerService; 
@GetMapping
public List<Product> getAllProduct(){
return productService.getAllProducts();
}
@GetMapping("/{id}")
public Product getProductById(@PathVariable Long id) {
return productService.getProductById(id);
}
@PostMapping
public Product createProduct(@RequestBody Product product) {
Product saveProduct = productService.saveProudct(product);
kafkaProducerService.sendMessage("Created Product with name: "+saveProduct.getName()+", price: "+saveProduct.getPrice());
return saveProduct;
}
@PutMapping("/{id}")
public Product updateProduct(@PathVariable Long id, @RequestBody Product product) {
Product existingProduct = productService.getProductById(id);
if(existingProduct !=null) {
product.setId(id);
return productService.saveProudct(product);
}
return null;
}
@DeleteMapping("/{id}")
public void deleteProduct(@PathVariable Long id) {
productService.deleteProduct(id);
}
}


package com.abg.uniapi.kafka;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.stereotype.Service;

@Service
public class KafkaConsumerListener {
@KafkaListener(topics = "products", groupId = "group_id")
    public void consume(String message) {
        System.out.println("Consumed message: " + message);
    }
}


package com.abg.uniapi.kafka;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;

@Service
public class KafkaProducerService {
private static final String TOPIC = "products";

    @Autowired
    private KafkaTemplate<String, Object> kafkaTemplate;

    public void sendMessage(String message) {
        kafkaTemplate.send(TOPIC, message);
    }
}


Application.properties

spring.application.name=uniapi


# MySQL Database Configuration

spring.datasource.url=jdbc:mysql://localhost:3306/uniapi

spring.datasource.username=root

spring.datasource.password=root

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver


# Hibernate specific properties

spring.jpa.database-platform=org.hibernate.dialect.MySQLDialect

spring.jpa.hibernate.ddl-auto=update


# Kafka Configuration

spring.kafka.bootstrap-servers=localhost:9092

spring.kafka.consumer.group-id=group_id

spring.kafka.consumer.auto-offset-reset=earliest



server.port=8084


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>3.3.2-SNAPSHOT</version>

<relativePath/> <!-- lookup parent from repository -->

</parent>

<groupId>com.abg</groupId>

<artifactId>uniapi</artifactId>

<version>0.0.1-SNAPSHOT</version>

<name>uniapi</name>

<description>Role Management project for Spring Boot</description>

<url/>

<licenses>

<license/>

</licenses>

<developers>

<developer/>

</developers>

<scm>

<connection/>

<developerConnection/>

<tag/>

<url/>

</scm>

<properties>

<java.version>17</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-thymeleaf</artifactId>

</dependency>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-web</artifactId>

</dependency>

<dependency>

<groupId>org.apache.kafka</groupId>

<artifactId>kafka-streams</artifactId>

</dependency>

<dependency>

<groupId>org.springframework.kafka</groupId>

<artifactId>spring-kafka</artifactId>

</dependency>

<dependency>

<groupId>org.springframework.session</groupId>

<artifactId>spring-session-core</artifactId>

</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>

<dependency>

<groupId>org.springframework.kafka</groupId>

<artifactId>spring-kafka-test</artifactId>

<scope>test</scope>

</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>

<repositories>

<repository>

<id>spring-snapshots</id>

<name>Spring Snapshots</name>

<url>https://repo.spring.io/snapshot</url>

<releases>

<enabled>false</enabled>

</releases>

</repository>

</repositories>

<pluginRepositories>

<pluginRepository>

<id>spring-snapshots</id>

<name>Spring Snapshots</name>

<url>https://repo.spring.io/snapshot</url>

<releases>

<enabled>false</enabled>

</releases>

</pluginRepository>

</pluginRepositories>


</project>