feat: Microservices pattern : Self-Registration (#3245)

* Services and information

* README.md updated

* unit tests added for controllers

* pom.xml, clean up, java version update and slf4j logging added

* pom files updated

* Removed children maven wrappers

* Microservice-self-registration added to pom file

* POM file updated for self-registration

* pom file updated

* Update pom.xml

correction to mudule name

* SonarQube comments addressed

* sonarqube comments on coverage addressed

* Code coverage for custom health checks

* changes for unit tests

* changes for unit tests

* changes to for code coverage improvement
This commit is contained in:
Naman Srivastava authored and GitHub committed 2026-02-08 20:40:07 +02:00
1 parent 3ba2560e09
commit 812e4cd6a2
39 files changed
+1049

No files matched your search

@@ -0,0 +1,2 @@
/mvnw text eol=lf
*.cmd text eol=crlf
@@ -0,0 +1,33 @@
HELP.md
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/
### VS Code ###
.vscode/
@@ -0,0 +1,69 @@
<?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.4.4</version>
<relativePath/> </parent>
<groupId>com.learning</groupId>
<artifactId>greetingservice</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>greetingservice</name>
<description>greetingservice</description>
<properties>
<spring-cloud.version>2024.0.1</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.38</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
@@ -0,0 +1,17 @@
package com.learning.greetingservice;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.context.annotation.ComponentScan;
@SpringBootApplication
@EnableDiscoveryClient
@ComponentScan("com.learning.greetingservice.controller")
public class GreetingserviceApplication {
public static void main(String[] args) {
SpringApplication.run(GreetingserviceApplication.class, args);
}
}
@@ -0,0 +1,41 @@
package com.learning.greetingservice;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Component("myCustomHealthCheck")
public class MyCustomHealthCheck implements HealthIndicator {
private static final Logger log = LoggerFactory.getLogger(MyCustomHealthCheck.class);
private volatile boolean isHealthy = true;
@Scheduled(fixedRate = 5000) // Run every 5 seconds
public void updateHealthStatus() {
// Perform checks here to determine the current health
// For example, check database connectivity, external service availability, etc.
isHealthy = performHealthCheck();
log.info("Update health status : {}", isHealthy);
}
boolean performHealthCheck() {
boolean current = System.currentTimeMillis() % 10000 < 5000; // Simulate fluctuating health
log.debug("Performing health check, current status: {}", current);
return current; // Simulate fluctuating health
}
@Override
public Health health() {
if (isHealthy) {
log.info("Health check successful, service is UP");
return Health.up().withDetail("message", "Service is running and scheduled checks are OK").build();
} else {
log.warn("Health check failed, service is DOWN");
return Health.down().withDetail("error", "Scheduled health checks failed").build();
}
}
}
@@ -0,0 +1,13 @@
package com.learning.greetingservice.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class GreetingsController {
@GetMapping("/greeting")
public String getGreeting() {
return "Hello";
}
}
@@ -0,0 +1,22 @@
server:
port: 8081
spring:
application:
name: greetingservice
eureka:
client:
service-url.defaultZone: http://localhost:8761/eureka
management:
endpoint:
health:
show-details: always
web:
exposure:
include: health
logging:
file:
name: application.log
@@ -0,0 +1,17 @@
package com.learning.greetingservice;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class GreetingserviceApplicationTests {
@Test
void contextLoads() {
// This is a basic integration test that checks if the Spring Application Context loads successfully.
// If the context loads without any exceptions, the test is considered passing.
// It is often left empty as the act of loading the context is the primary verification.
// You can add specific assertions here if you want to verify the presence or state of certain beans.
}
}
@@ -0,0 +1,22 @@
package com.learning.greetingservice;
import org.junit.jupiter.api.Test;
import org.springframework.boot.actuate.health.Health;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.boot.actuate.health.Status;
import static org.junit.jupiter.api.Assertions.*;
class MyCustomHealthCheckTest {
@Test
void testHealthUp() {
MyCustomHealthCheck healthCheck = new MyCustomHealthCheck();
// Simulate a healthy state
ReflectionTestUtils.setField(healthCheck, "isHealthy", true);
Health health = healthCheck.health();
assertEquals(Status.UP, health.getStatus());
assertTrue(health.getDetails().containsKey("message"));
assertEquals("Service is running and scheduled checks are OK", health.getDetails().get("message"));
}
}
@@ -0,0 +1,36 @@
package com.learning.greetingservice.controller;
import com.learning.greetingservice.GreetingserviceApplication;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
@SpringBootTest(classes = GreetingserviceApplication.class)
@AutoConfigureMockMvc
@ActiveProfiles("test")
class GreetingControllerTest {
@Autowired
private MockMvc mockMvc;
@Test
void shouldReturnGreeting() throws Exception{
mockMvc.perform(MockMvcRequestBuilders.get("/greeting")
.accept(MediaType.TEXT_PLAIN))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.content().string("Hello"));
}
@Test
void shouldReturnHealthStatusUp() throws Exception{
mockMvc.perform(MockMvcRequestBuilders.get("/actuator/health"))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.content().string(org.hamcrest.Matchers.containsString("\"status\":\"UP\"")));
}
}