diff --git a/micrometer-quickstart/src/main/java/org/acme/micrometer/ExampleResource.java b/micrometer-quickstart/src/main/java/org/acme/micrometer/ExampleResource.java index 0629a31574..20ebaa9fd8 100644 --- a/micrometer-quickstart/src/main/java/org/acme/micrometer/ExampleResource.java +++ b/micrometer-quickstart/src/main/java/org/acme/micrometer/ExampleResource.java @@ -17,7 +17,7 @@ public class ExampleResource { private final MeterRegistry registry; - LinkedList list = new LinkedList<>(); + private final LinkedList list = new LinkedList<>(); // Update the constructor to create the gauge ExampleResource(MeterRegistry registry) { diff --git a/micrometer-quickstart/src/main/java/org/acme/micrometer/TemperatureResource.java b/micrometer-quickstart/src/main/java/org/acme/micrometer/TemperatureResource.java new file mode 100644 index 0000000000..f8426d1aed --- /dev/null +++ b/micrometer-quickstart/src/main/java/org/acme/micrometer/TemperatureResource.java @@ -0,0 +1,121 @@ +package org.acme.micrometer; + +import java.util.LinkedList; + +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.PathParam; + +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tags; +import io.micrometer.core.instrument.Timer; +import io.micrometer.core.instrument.DistributionSummary; + +/** + * REST API for monitoring and recording temperature readings. + */ +@Path("/temperature") +@Produces("text/plain") +public class TemperatureResource { + + private final MeterRegistry registry; + private LinkedList temperatures = new LinkedList<>(); + + DistributionSummary temperatureSummary; + + /** + * Constructs a TemperatureResource. + * Initializes the monitoring tools for temperatures. + * + * @param registry MeterRegistry for recording metrics. + */ + public TemperatureResource(MeterRegistry registry) { + this.registry = registry; + // Summary to record temperature readings and their distribution. + temperatureSummary = registry.summary("temperature.readings", Tags.of("unit", "celsius")); + // Gauge to monitor the size of the temperature list in real-time. + registry.gaugeCollectionSize("temperature.list.size", Tags.empty(), temperatures); + } + + /** + * Adds a temperature reading to the recorded list and updates the summary metric. + * + * @param temp The temperature to add. + * @return A message confirming the addition of the temperature. + */ + @GET + @Path("/add/{temp}") + public String addTemperature(@PathParam("temp") double temp) { + temperatures.add(temp); + temperatureSummary.record(temp); + return "Temperature added: " + temp; + } + + /** + * Calculates the average of all recorded temperatures. + * Uses a timer to measure the time taken to perform this calculation. + * + * @return The average temperature or a message if no temperatures are recorded. + */ + @GET + @Path("/average") + public String calculateAverage() { + Timer timer = registry.timer("temperature.calculation.average"); + return timer.record(() -> { + if (temperatures.isEmpty()) { + return "No temperatures recorded."; + } + double sum = 0; + for (Double t : temperatures) { + sum += t; + } + double average = sum / temperatures.size(); + return "Average temperature: " + average + " Celsius"; + }); + } + + /** + * Finds the maximum temperature from the recorded list. + * Measurement is timed for performance analysis. + * + * @return The maximum temperature or a message if no temperatures are recorded. + */ + @GET + @Path("/max") + public String findMaxTemperature() { + Timer timer = registry.timer("temperature.calculation.max"); + return timer.record(() -> { + return temperatures.stream() + .max(Double::compare) + .map(maxTemp -> "Maximum temperature: " + maxTemp + " Celsius") + .orElse("No temperatures recorded."); + }); + } + + /** + * Finds the minimum temperature from the recorded list. + * Measurement is timed for performance analysis. + * + * @return The minimum temperature or a message if no temperatures are recorded. + */ + @GET + @Path("/min") + public String findMinTemperature() { + Timer timer = registry.timer("temperature.calculation.min"); + return timer.record(() -> { + return temperatures.stream() + .min(Double::compare) + .map(minTemp -> "Minimum temperature: " + minTemp + " Celsius") + .orElse("No temperatures recorded."); + }); + } + + // New endpoint to reset the temperatures list + @GET + @Path("/reset") + public String resetTemperatures() { + temperatures.clear(); + return "Temperatures reset successfully."; + } +} diff --git a/micrometer-quickstart/src/test/java/org/acme/micrometer/TemperatureResourceTest.java b/micrometer-quickstart/src/test/java/org/acme/micrometer/TemperatureResourceTest.java new file mode 100644 index 0000000000..463a13f9db --- /dev/null +++ b/micrometer-quickstart/src/test/java/org/acme/micrometer/TemperatureResourceTest.java @@ -0,0 +1,85 @@ +package org.acme.micrometer; + +import static io.restassured.RestAssured.get; +import static io.restassured.RestAssured.when; +import static org.hamcrest.CoreMatchers.containsString; + +import org.junit.jupiter.api.Test; + +import io.quarkus.test.junit.QuarkusTest; +import io.restassured.http.Header; + +/** + * Test class for TemperatureResource using REST-assured. + */ +@QuarkusTest +public class TemperatureResourceTest { + + @BeforeEach + void resetTemperatures() { + get("/temperature/reset").then().assertThat().statusCode(200); + } + + @Test + void testAddTemperature() { + when().get("/temperature/add/20.5").then().statusCode(200) + .body(containsString("Temperature added: 20.5")); + when().get("/temperature/add/22.3").then().statusCode(200) + .body(containsString("Temperature added: 22.3")); + } + + @Test + void testAverageTemperature() { + // Add temperatures first + get("/temperature/add/20.5"); + get("/temperature/add/22.3"); + get("/temperature/add/25.1"); + + // Check the average calculation + when().get("/temperature/average").then().statusCode(200) + .body(containsString("Average temperature: 22.633333333333333 Celsius")); + } + + @Test + void testMaxTemperature() { + // Add temperatures first + get("/temperature/add/20.5"); + get("/temperature/add/22.3"); + get("/temperature/add/25.1"); + + // Check the maximum temperature calculation + when().get("/temperature/max").then().statusCode(200) + .body(containsString("Maximum temperature: 25.1 Celsius")); + } + + @Test + void testMinTemperature() { + // Add temperatures first + get("/temperature/add/20.5"); + get("/temperature/add/22.3"); + get("/temperature/add/25.1"); + + // Check the minimum temperature calculation + when().get("/temperature/min").then().statusCode(200) + .body(containsString("Minimum temperature: 20.5 Celsius")); + } + + @Test + void testTemperatureMetrics() { + // Trigger endpoints to populate metrics + get("/temperature/add/20.5"); + get("/temperature/add/22.3"); + get("/temperature/add/25.1"); + get("/temperature/average"); + get("/temperature/max"); + get("/temperature/min"); + + // Check metrics for recorded operations + when().get("/q/metrics").then().statusCode(200) + .body(containsString("temperature_readings_sum")) + .body(containsString("temperature_readings_count 3.0")) + .body(containsString("temperature_calculation_average_seconds_count 1.0")) + .body(containsString("temperature_calculation_max_seconds_count 1.0")) + .body(containsString("temperature_calculation_min_seconds_count 1.0")); + } +}