diff --git a/build.gradle b/build.gradle index e3abbbf..57d35b7 100644 --- a/build.gradle +++ b/build.gradle @@ -15,6 +15,7 @@ dependencies { compile("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.9.1") compile("org.springframework.boot:spring-boot-starter-jdbc") compile("mysql:mysql-connector-java:6.0.6") + compile("org.springframework.boot:spring-boot-starter-actuator") testCompile("org.springframework.boot:spring-boot-starter-test") } @@ -23,12 +24,16 @@ def developmentDbUrl = "jdbc:mysql://localhost:3306/tracker_dev?user=tracker&use bootRun.environment([ "WELCOME_MESSAGE": "hello", "SPRING_DATASOURCE_URL": developmentDbUrl, + "MANAGEMENT_SECURITY_ENABLED": false, + ]) def testDbUrl = "jdbc:mysql://localhost:3306/tracker_test?user=tracker&useSSL=false&useTimezone=true&serverTimezone=UTC&useLegacyDatetimeCode=false" test.environment([ "WELCOME_MESSAGE": "Hello from test", "SPRING_DATASOURCE_URL": testDbUrl, + "MANAGEMENT_SECURITY_ENABLED": false, + ]) flyway { @@ -41,3 +46,7 @@ flyway { task testMigrate(type: FlywayMigrateTask) { url = testDbUrl } + +springBoot { + buildInfo() +} diff --git a/src/main/java/io/pivotal/pal/tracker/InMemoryTimeEntryRepository.java b/src/main/java/io/pivotal/pal/tracker/InMemoryTimeEntryRepository.java index 3cc017a..179ba2c 100644 --- a/src/main/java/io/pivotal/pal/tracker/InMemoryTimeEntryRepository.java +++ b/src/main/java/io/pivotal/pal/tracker/InMemoryTimeEntryRepository.java @@ -5,7 +5,6 @@ import java.util.*; import java.util.stream.Collectors; - @Repository public class InMemoryTimeEntryRepository implements TimeEntryRepository { private Map data; diff --git a/src/main/java/io/pivotal/pal/tracker/JdbcTimeEntryRepository.java b/src/main/java/io/pivotal/pal/tracker/JdbcTimeEntryRepository.java index 7e9b4c3..1d70432 100644 --- a/src/main/java/io/pivotal/pal/tracker/JdbcTimeEntryRepository.java +++ b/src/main/java/io/pivotal/pal/tracker/JdbcTimeEntryRepository.java @@ -7,6 +7,7 @@ import org.springframework.jdbc.core.namedparam.NamedParameterUtils; import org.springframework.jdbc.support.GeneratedKeyHolder; import org.springframework.jdbc.support.KeyHolder; +import org.springframework.stereotype.Repository; import javax.sql.DataSource; import java.sql.Date; @@ -15,6 +16,7 @@ import static java.sql.Statement.RETURN_GENERATED_KEYS; + public class JdbcTimeEntryRepository implements TimeEntryRepository { diff --git a/src/main/java/io/pivotal/pal/tracker/TimeEntryController.java b/src/main/java/io/pivotal/pal/tracker/TimeEntryController.java index 7882034..3e4582a 100644 --- a/src/main/java/io/pivotal/pal/tracker/TimeEntryController.java +++ b/src/main/java/io/pivotal/pal/tracker/TimeEntryController.java @@ -1,12 +1,17 @@ package io.pivotal.pal.tracker; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.actuate.metrics.CounterService; +import org.springframework.boot.actuate.metrics.GaugeService; +import org.springframework.context.annotation.Bean; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import org.springframework.web.client.RestTemplate; +import javax.sql.DataSource; import java.util.List; import java.util.Objects; @@ -14,18 +19,32 @@ @RequestMapping(value = "/time-entries") public class TimeEntryController { - - @Autowired + private GaugeService gauge; + private CounterService counter; private TimeEntryRepository timeEntryRepository; - public TimeEntryController(TimeEntryRepository timeEntryRepository) { + public TimeEntryController( + TimeEntryRepository timeEntryRepository, + CounterService counter, + GaugeService gauge + ) { this.timeEntryRepository = timeEntryRepository; + this.counter = counter; + this.gauge = gauge; } + + + @PostMapping public ResponseEntity create(@RequestBody TimeEntry timeEntryToCreate) { - return ResponseEntity.status(HttpStatus.CREATED).body(this.timeEntryRepository.create(timeEntryToCreate)); + timeEntryToCreate = this.timeEntryRepository.create(timeEntryToCreate); + + counter.increment("TimeEntry.created"); + gauge.submit("timeEntries.count", timeEntryRepository.list().size()); + + return ResponseEntity.status(HttpStatus.CREATED).body(timeEntryToCreate); } @@ -34,6 +53,7 @@ public ResponseEntity read(@PathVariable long timeEntryId) { TimeEntry timeEntry = this.timeEntryRepository.find(timeEntryId); if (Objects.nonNull(timeEntry)) { + counter.increment("TimeEntry.read"); return ResponseEntity.ok(timeEntry); } @@ -42,6 +62,7 @@ public ResponseEntity read(@PathVariable long timeEntryId) { @GetMapping public ResponseEntity> list() { + counter.increment("TimeEntry.listed"); return ResponseEntity.ok(this.timeEntryRepository.list()); } @@ -51,6 +72,7 @@ public ResponseEntity update(@PathVariable long timeEntryId, @RequestBody TimeEn timeEntry = this.timeEntryRepository.update(timeEntryId, timeEntry); if (Objects.nonNull(timeEntry)) { + counter.increment("TimeEntry.updated"); return ResponseEntity.ok(timeEntry); } @@ -64,6 +86,8 @@ public ResponseEntity update(@PathVariable long timeEntryId, @RequestBody TimeEn @DeleteMapping("/{timeEntryId}") public ResponseEntity delete(@PathVariable long timeEntryId) { this.timeEntryRepository.delete(timeEntryId); + counter.increment("TimeEntry.deleted"); + gauge.submit("timeEntries.count", timeEntryRepository.list().size()); return ResponseEntity.noContent().build(); } diff --git a/src/main/java/io/pivotal/pal/tracker/TimeEntryHealthIndicator.java b/src/main/java/io/pivotal/pal/tracker/TimeEntryHealthIndicator.java new file mode 100644 index 0000000..35741e2 --- /dev/null +++ b/src/main/java/io/pivotal/pal/tracker/TimeEntryHealthIndicator.java @@ -0,0 +1,30 @@ +package io.pivotal.pal.tracker; + + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.actuate.health.Health; +import org.springframework.boot.actuate.health.HealthIndicator; +import org.springframework.stereotype.Component; + +@Component +public class TimeEntryHealthIndicator implements HealthIndicator { + + private static final int MAX_TIME_ENTRIES = 5; + + @Autowired + private TimeEntryRepository timeEntryRepository; + + + @Override + public Health health() { + Health.Builder builder = new Health.Builder(); + + if(timeEntryRepository.list().size() < MAX_TIME_ENTRIES) { + builder.up(); + } else { + builder.down(); + } + + return builder.build(); + } +} diff --git a/src/main/java/io/pivotal/pal/tracker/TimeEntryRepository.java b/src/main/java/io/pivotal/pal/tracker/TimeEntryRepository.java index 65048e1..33f2755 100644 --- a/src/main/java/io/pivotal/pal/tracker/TimeEntryRepository.java +++ b/src/main/java/io/pivotal/pal/tracker/TimeEntryRepository.java @@ -5,7 +5,6 @@ import java.util.List; - @Repository public interface TimeEntryRepository { TimeEntry create(TimeEntry timeEntry); diff --git a/src/test/java/test/pivotal/pal/tracker/TimeEntryControllerTest.java b/src/test/java/test/pivotal/pal/tracker/TimeEntryControllerTest.java index 4e033f1..8a1aed5 100644 --- a/src/test/java/test/pivotal/pal/tracker/TimeEntryControllerTest.java +++ b/src/test/java/test/pivotal/pal/tracker/TimeEntryControllerTest.java @@ -6,6 +6,8 @@ import io.pivotal.pal.tracker.TimeEntryRepository; import org.junit.Before; import org.junit.Test; +import org.springframework.boot.actuate.metrics.CounterService; +import org.springframework.boot.actuate.metrics.GaugeService; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -21,12 +23,15 @@ public class TimeEntryControllerTest { private TimeEntryRepository timeEntryRepository; private TimeEntryController controller; - + private GaugeService gauge; + private CounterService counter; @Before public void setUp() throws Exception { timeEntryRepository = mock(TimeEntryRepository.class); - controller = new TimeEntryController(timeEntryRepository); + gauge = mock(GaugeService.class); + counter = mock(CounterService.class); + controller = new TimeEntryController(timeEntryRepository, counter, gauge); } @Test diff --git a/src/test/java/test/pivotal/pal/trackerapi/TimeEntryApiTest.java b/src/test/java/test/pivotal/pal/trackerapi/TimeEntryApiTest.java index 4025450..5300763 100644 --- a/src/test/java/test/pivotal/pal/trackerapi/TimeEntryApiTest.java +++ b/src/test/java/test/pivotal/pal/trackerapi/TimeEntryApiTest.java @@ -2,14 +2,17 @@ import com.jayway.jsonpath.DocumentContext; import com.mysql.cj.jdbc.MysqlDataSource; +import io.pivotal.pal.tracker.JdbcTimeEntryRepository; import io.pivotal.pal.tracker.PalTrackerApplication; import io.pivotal.pal.tracker.TimeEntry; +import io.pivotal.pal.tracker.TimeEntryRepository; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.context.annotation.Bean; import org.springframework.http.HttpEntity; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; @@ -18,6 +21,7 @@ import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.test.context.junit4.SpringRunner; +import javax.sql.DataSource; import java.time.LocalDate; import java.util.Collection; import java.util.TimeZone; @@ -48,6 +52,9 @@ public void setUp() throws Exception { TimeZone.setDefault(TimeZone.getTimeZone("UTC")); } + + + @Test public void testCreate() throws Exception {