Merge branch 'gh-8042'
commit
3fb86b261e
@ -0,0 +1,45 @@
|
||||
[[audit-events]]
|
||||
= Audit Events (`auditevents`)
|
||||
|
||||
The `auditevents` endpoint provides information about the application's audit events.
|
||||
|
||||
|
||||
|
||||
[[audit-events-retrieving]]
|
||||
== Retrieving Audit Events
|
||||
|
||||
To retrieve the audit events, make a `GET` request to `/application/auditevents`, as shown
|
||||
in the following curl-based example:
|
||||
|
||||
include::{snippets}auditevents/filtered/curl-request.adoc[]
|
||||
|
||||
The preceding example retrieves `logout` events for the principal, `alice`, that occurred
|
||||
after 09:37 on 7 November 2017 in the UTC timezone. The resulting response is similar to
|
||||
the following:
|
||||
|
||||
include::{snippets}auditevents/filtered/http-response.adoc[]
|
||||
|
||||
|
||||
|
||||
[[audit-events-retrieving-query-parameters]]
|
||||
=== Query Parameters
|
||||
|
||||
The endpoint uses query parameters to limit the events that it returns. The following
|
||||
table shows the supported query parameters:
|
||||
|
||||
[cols="2,4"]
|
||||
include::{snippets}auditevents/filtered/request-parameters.adoc[]
|
||||
|
||||
The `after` parameter is required. You can also use one or both of the `principal` and
|
||||
`type` parameters to further limit the results.
|
||||
|
||||
|
||||
|
||||
[[audit-events-retrieving-response-structure]]
|
||||
=== Response Structure
|
||||
|
||||
The response contains details of all of the audit events that matched the query. The
|
||||
following table describes the structure of the response:
|
||||
|
||||
[cols="2,1,3"]
|
||||
include::{snippets}auditevents/after/response-fields.adoc[]
|
@ -0,0 +1,29 @@
|
||||
[[beans]]
|
||||
= Beans (`beans`)
|
||||
|
||||
The `beans` endpoint provides information about the application's beans.
|
||||
|
||||
|
||||
|
||||
[[beans-retrieving]]
|
||||
== Retrieving the Beans
|
||||
|
||||
To retrieve the beans, make a `GET` request to `/application/beans`, as shown in the
|
||||
following curl-based example:
|
||||
|
||||
include::{snippets}beans/curl-request.adoc[]
|
||||
|
||||
The resulting response is similar to the following:
|
||||
|
||||
include::{snippets}beans/http-response.adoc[]
|
||||
|
||||
|
||||
|
||||
[[beans-retrieving-response-structure]]
|
||||
=== Response Structure
|
||||
|
||||
The response contains details of the application's beans. The following table describes
|
||||
the structure of the response:
|
||||
|
||||
[cols="2,1,3"]
|
||||
include::{snippets}beans/response-fields.adoc[]
|
@ -0,0 +1,30 @@
|
||||
[[conditions]]
|
||||
= Conditions Evaluation Report (`conditions`)
|
||||
|
||||
The `conditions` endpoint provides information about the evaluation of conditions on
|
||||
configuration and auto-configuration classes.
|
||||
|
||||
|
||||
|
||||
[[conditions-retrieving]]
|
||||
== Retrieving the Report
|
||||
|
||||
To retrieve the report, make a `GET` request to `/application/conditions`, as shown in
|
||||
the following curl-based example:
|
||||
|
||||
include::{snippets}conditions/curl-request.adoc[]
|
||||
|
||||
The resulting response is similar to the following:
|
||||
|
||||
include::{snippets}conditions/http-response.adoc[]
|
||||
|
||||
|
||||
|
||||
[[conditions-retrieving-response-structure]]
|
||||
=== Response Structure
|
||||
|
||||
The response contains details of the application's condition evaluation. The following
|
||||
table describes the structure of the response:
|
||||
|
||||
[cols="3,1,3"]
|
||||
include::{snippets}conditions/response-fields.adoc[]
|
@ -0,0 +1,30 @@
|
||||
[[configprops]]
|
||||
= Configuration Properties (`configprops`)
|
||||
|
||||
The `configprops` endpoint provides information about the application's
|
||||
`@ConfigurationProperties` beans.
|
||||
|
||||
|
||||
|
||||
[[configprops-retrieving]]
|
||||
== Retrieving the `@ConfigurationProperties` Bean
|
||||
|
||||
To retrieve the `@ConfigurationProperties` beans, make a `GET` request to
|
||||
`/application/configprops`, as shown in the following curl-based example:
|
||||
|
||||
include::{snippets}configprops/curl-request.adoc[]
|
||||
|
||||
The resulting response is similar to the following:
|
||||
|
||||
include::{snippets}configprops/http-response.adoc[]
|
||||
|
||||
|
||||
|
||||
[[configprops-retrieving-response-structure]]
|
||||
=== Response Structure
|
||||
|
||||
The response contains details of the application's `@ConfigurationProperties` beans. The
|
||||
following table describes the structure of the response:
|
||||
|
||||
[cols="2,1,3"]
|
||||
include::{snippets}configprops/response-fields.adoc[]
|
@ -0,0 +1,55 @@
|
||||
[[env]]
|
||||
= Environment (`env`)
|
||||
|
||||
The `env` endpoint provides information about the application's `Environment`.
|
||||
|
||||
|
||||
|
||||
[[env-entire]]
|
||||
== Retrieving the Entire Environment
|
||||
|
||||
To retrieve the entire environment, make a `GET` request to `/application/env`, as shown in
|
||||
the following curl-based example:
|
||||
|
||||
include::{snippets}env/all/curl-request.adoc[]
|
||||
|
||||
The resulting response is similar to the following:
|
||||
|
||||
include::{snippets}env/all/http-response.adoc[]
|
||||
|
||||
|
||||
|
||||
[[env-entire-response-structure]]
|
||||
=== Response Structure
|
||||
|
||||
The response contains details of the application's `Environment`. The following table
|
||||
describes the structure of the response:
|
||||
|
||||
[cols="3,1,3"]
|
||||
include::{snippets}env/all/response-fields.adoc[]
|
||||
|
||||
|
||||
|
||||
[[env-single-property]]
|
||||
== Retrieving a Single Property
|
||||
|
||||
To retrieve a single property, make a `GET` request to `/application/env/{property.name}`,
|
||||
as shown in the following curl-based example:
|
||||
|
||||
include::{snippets}env/single/curl-request.adoc[]
|
||||
|
||||
The preceding example retrieves information about the property named
|
||||
`com.example.cache.max-size`. The resulting response is similar to the following:
|
||||
|
||||
include::{snippets}env/single/http-response.adoc[]
|
||||
|
||||
|
||||
|
||||
[[env-single-response-structure]]
|
||||
=== Response Structure
|
||||
|
||||
The response contains details of the requested property. The following table describes the
|
||||
structure of the response:
|
||||
|
||||
[cols="3,1,3"]
|
||||
include::{snippets}env/single/response-fields.adoc[]
|
@ -0,0 +1,29 @@
|
||||
[[flyway]]
|
||||
= Flyway (`flyway`)
|
||||
|
||||
The `flyway` endpoint provides information about database migrations performed by Flyway.
|
||||
|
||||
|
||||
|
||||
[[flyway-retrieving]]
|
||||
== Retrieving the Migrations
|
||||
|
||||
To retrieve the migrations, make a `GET` request to `/application/flyway`, as shown in the
|
||||
following curl-based example:
|
||||
|
||||
include::{snippets}flyway/curl-request.adoc[]
|
||||
|
||||
The resulting response is similar to the following:
|
||||
|
||||
include::{snippets}flyway/http-response.adoc[]
|
||||
|
||||
|
||||
|
||||
[[flyway-retrieving-response-structure]]
|
||||
=== Response Structure
|
||||
|
||||
The response contains details of the application's Flyway migrations. The following table
|
||||
describes the structure of the response:
|
||||
|
||||
[cols="2,1,3"]
|
||||
include::{snippets}flyway/response-fields.adoc[]
|
@ -0,0 +1,29 @@
|
||||
[[health]]
|
||||
= Health (`health`)
|
||||
|
||||
The `health` endpoint provides detailed information about the health of the application.
|
||||
|
||||
|
||||
|
||||
[[health-retrieving]]
|
||||
== Retrieving the Health
|
||||
|
||||
To retrieve the health of the application, make a `GET` request to `/application/health`,
|
||||
as shown in the following curl-based example:
|
||||
|
||||
include::{snippets}health/curl-request.adoc[]
|
||||
|
||||
The resulting response is similar to the following:
|
||||
|
||||
include::{snippets}health/http-response.adoc[]
|
||||
|
||||
|
||||
|
||||
[[health-retrieving-response-structure]]
|
||||
=== Response Structure
|
||||
|
||||
The response contains details of the health of the application. The following table
|
||||
describes the structure of the response:
|
||||
|
||||
[cols="2,1,3"]
|
||||
include::{snippets}health/response-fields.adoc[]
|
@ -0,0 +1,20 @@
|
||||
[[heapdump]]
|
||||
= Heap Dump (`heapdump`)
|
||||
|
||||
The `heapdump` endpoint provides a heap dump from the application's JVM.
|
||||
|
||||
|
||||
|
||||
[[heapdump-retrieving]]
|
||||
== Retrieving the Heap Dump
|
||||
|
||||
To retrieve the heap dump, make a `GET` request to `/application/heapdump`. The response
|
||||
is binary data in https://docs.oracle.com/javase/8/docs/technotes/samples/hprof.html[
|
||||
HPROF] format and can be large. Typically, you should save the response to disk for
|
||||
subsequent analysis. When using curl, this can be achieved by using the `-O` option,
|
||||
as shown in the following example:
|
||||
|
||||
include::{snippets}heapdump/curl-request.adoc[]
|
||||
|
||||
The preceding example results in a file named `heapdump` being written to the current
|
||||
working directory.
|
@ -0,0 +1,47 @@
|
||||
[[info]]
|
||||
= Info (`info`)
|
||||
|
||||
The `info` endpoint provides general information about the application.
|
||||
|
||||
|
||||
|
||||
[[info-retrieving]]
|
||||
== Retrieving the Info
|
||||
|
||||
To retrieve the information about the application, make a `GET` request to
|
||||
`/application/info`, as shown in the following curl-based example:
|
||||
|
||||
include::{snippets}info/curl-request.adoc[]
|
||||
|
||||
The resulting response is similar to the following:
|
||||
|
||||
include::{snippets}info/http-response.adoc[]
|
||||
|
||||
|
||||
|
||||
[[info-retrieving-response-structure]]
|
||||
=== Response Structure
|
||||
|
||||
The response contains general information about the application. Each section of the
|
||||
response is contributed by an `InfoContributor`. Spring Boot provides `build` and `git`
|
||||
contributions.
|
||||
|
||||
|
||||
|
||||
[[info-retrieving-response-structure-build]]
|
||||
==== `build` Response Structure
|
||||
|
||||
The following table describe the structure of the `build` section of the response:
|
||||
|
||||
[cols="2,1,3"]
|
||||
include::{snippets}info/response-fields-beneath-build.adoc[]
|
||||
|
||||
|
||||
|
||||
[[info-retrieving-response-structure-git]]
|
||||
==== `git` Response Structure
|
||||
|
||||
The following table describes the structure of the `git` section of the response:
|
||||
|
||||
[cols="2,1,3"]
|
||||
include::{snippets}info/response-fields-beneath-git.adoc[]
|
@ -0,0 +1,30 @@
|
||||
[[liquibase]]
|
||||
= Liquibase (`liquibase`)
|
||||
|
||||
The `liquibase` endpoint provides information about database change sets applied by
|
||||
Liquibase.
|
||||
|
||||
|
||||
|
||||
[[liquibase-retrieving]]
|
||||
== Retrieving the Changes
|
||||
|
||||
To retrieve the changes, make a `GET` request to `/application/liquibase`, as shown in the
|
||||
following curl-based example:
|
||||
|
||||
include::{snippets}liquibase/curl-request.adoc[]
|
||||
|
||||
The resulting response is similar to the following:
|
||||
|
||||
include::{snippets}liquibase/http-response.adoc[]
|
||||
|
||||
|
||||
|
||||
[[liquibase-retrieving-response-structure]]
|
||||
=== Response Structure
|
||||
|
||||
The response contains details of the application's Liquibase change sets. The following
|
||||
table describes the structure of the response:
|
||||
|
||||
[cols="2,1,3"]
|
||||
include::{snippets}liquibase/response-fields.adoc[]
|
@ -0,0 +1,35 @@
|
||||
[[log-file]]
|
||||
= Log File (`logfile`)
|
||||
|
||||
The `logfile` endpoint provides access to the contents of the application's log file.
|
||||
|
||||
|
||||
|
||||
[[logfile-retrieving]]
|
||||
== Retrieving the Log File
|
||||
|
||||
To retrieve the log file, make a `GET` request to `/application/logfile`, as shown in the
|
||||
following curl-based example:
|
||||
|
||||
include::{snippets}logfile/entire/curl-request.adoc[]
|
||||
|
||||
The resulting response is similar to the following:
|
||||
|
||||
include::{snippets}logfile/entire/http-response.adoc[]
|
||||
|
||||
|
||||
|
||||
[[logfile-retrieving-part]]
|
||||
== Retrieving Part of the Log File
|
||||
|
||||
NOTE: Retrieving part of the log file is not supported when using Jersey.
|
||||
|
||||
To retrieve part of the log file, make a `GET` request to `/application/logfile` by using
|
||||
the `Range` header, as shown in the following curl-based example:
|
||||
|
||||
include::{snippets}logfile/range/curl-request.adoc[]
|
||||
|
||||
The preceding example retrieves the first 1024 bytes of the log file. The resulting
|
||||
response is similar to the following:
|
||||
|
||||
include::{snippets}logfile/range/http-response.adoc[]
|
@ -0,0 +1,93 @@
|
||||
[[loggers]]
|
||||
= Loggers (`loggers`)
|
||||
|
||||
The `loggers` endpoint provides access to the application's loggers and the configuration
|
||||
of their levels.
|
||||
|
||||
|
||||
|
||||
[[loggers-all]]
|
||||
== Retrieving All Loggers
|
||||
|
||||
To retrieve the application's loggers, make a `GET` request to `/application/loggers`, as
|
||||
shown in the following curl-based example:
|
||||
|
||||
include::{snippets}loggers/all/curl-request.adoc[]
|
||||
|
||||
The resulting response is similar to the following:
|
||||
|
||||
include::{snippets}loggers/all/http-response.adoc[]
|
||||
|
||||
|
||||
|
||||
[[loggers-all-response-structure]]
|
||||
=== Response Structure
|
||||
|
||||
The response contains details of the application's loggers. The following table describes
|
||||
the structure of the response:
|
||||
|
||||
[cols="3,1,3"]
|
||||
include::{snippets}loggers/all/response-fields.adoc[]
|
||||
|
||||
|
||||
|
||||
[[loggers-single]]
|
||||
== Retrieving a Single Logger
|
||||
|
||||
To retrieve a single logger, make a `GET` request to `/application/loggers/{logger.name}`,
|
||||
as shown in the following curl-based example:
|
||||
|
||||
include::{snippets}loggers/single/curl-request.adoc[]
|
||||
|
||||
The preceding example retrieves information about the logger named `com.example`. The
|
||||
resulting response is similar to the following:
|
||||
|
||||
include::{snippets}loggers/single/http-response.adoc[]
|
||||
|
||||
|
||||
|
||||
[[loggerse-single-response-structure]]
|
||||
=== Response Structure
|
||||
|
||||
The response contains details of the requested logger. The following table describes the
|
||||
structure of the response:
|
||||
|
||||
[cols="3,1,3"]
|
||||
include::{snippets}loggers/single/response-fields.adoc[]
|
||||
|
||||
|
||||
|
||||
[[loggers-setting-level]]
|
||||
== Setting a Log Level
|
||||
|
||||
To set the level of a logger, make a `POST` request to
|
||||
`/application/loggers/{logger.name}` with a JSON body that specifies the configured level
|
||||
for the logger, as shown in the following curl-based example:
|
||||
|
||||
include::{snippets}loggers/set/curl-request.adoc[]
|
||||
|
||||
The preceding example sets the `configuredLevel` of the `com.example` logger to `DEBUG`.
|
||||
|
||||
|
||||
|
||||
[[loggers-setting-level-request-structure]]
|
||||
=== Request Structure
|
||||
|
||||
The request specifies the desired level of the logger. The following table describes the
|
||||
structure of the request:
|
||||
|
||||
[cols="3,1,3"]
|
||||
include::{snippets}loggers/set/request-fields.adoc[]
|
||||
|
||||
|
||||
|
||||
[[loggers-clearing-level]]
|
||||
== Clearing a Log Level
|
||||
|
||||
To clear the level of a logger, make a `POST` request to
|
||||
`/application/loggers/{logger.name}` with a JSON body containing an empty object, as shown
|
||||
in the following curl-based example:
|
||||
|
||||
include::{snippets}loggers/clear/curl-request.adoc[]
|
||||
|
||||
The preceding example clears the configured level of the `com.example` logger.
|
@ -0,0 +1,80 @@
|
||||
[[metrics]]
|
||||
= Metrics (`metrics`)
|
||||
|
||||
The `metrics` endpoint provides access to application metrics.
|
||||
|
||||
|
||||
|
||||
[[metrics-retrieving-names]]
|
||||
== Retrieving Metric Names
|
||||
|
||||
To retrieve the names of the available metrics, make a `GET` request to
|
||||
`/application/metrics`, as shown in the following curl-based example:
|
||||
|
||||
include::{snippets}metrics/names/curl-request.adoc[]
|
||||
|
||||
The resulting response is similar to the following:
|
||||
|
||||
include::{snippets}metrics/names/http-response.adoc[]
|
||||
|
||||
|
||||
|
||||
[[metrics-retrieving-names-response-structure]]
|
||||
=== Response Structure
|
||||
|
||||
The response contains details of the metric names. The following table describes the
|
||||
structure of the response:
|
||||
|
||||
[cols="3,1,2"]
|
||||
include::{snippets}metrics/names/response-fields.adoc[]
|
||||
|
||||
|
||||
|
||||
[[metrics-retrieving-metric]]
|
||||
== Retrieving a Metric
|
||||
|
||||
To retrieve a metric, make a `GET` request to `/application/metrics/{metric.name}`, as
|
||||
shown in the following curl-based example:
|
||||
|
||||
include::{snippets}metrics/metric/curl-request.adoc[]
|
||||
|
||||
The preceding example retrieves information about the metric named `jvm.memory.max`. The
|
||||
resulting response is similar to the following:
|
||||
|
||||
include::{snippets}metrics/metric/http-response.adoc[]
|
||||
|
||||
|
||||
|
||||
[[metrics-retrieving-metric-query-parameters]]
|
||||
=== Query Parameters
|
||||
|
||||
The endpoint uses query parameters to <<metrics-drilling-down,drill down>> into a metric
|
||||
by using its tags. The following table shows the single supported query parameter:
|
||||
|
||||
[cols="2,4"]
|
||||
include::{snippets}metrics/metric-with-tags/request-parameters.adoc[]
|
||||
|
||||
|
||||
|
||||
[[metrics-retrieving-metric-response-structure]]
|
||||
=== Response structure
|
||||
|
||||
The response contains details of the metric. The following table describes the structure
|
||||
of the response:
|
||||
|
||||
include::{snippets}metrics/metric/response-fields.adoc[]
|
||||
|
||||
|
||||
[[metrics-drilling-down]]
|
||||
== Drilling Down
|
||||
|
||||
To drill down into a metric, make a `GET` request to `/application/metrics/{metric.name}`
|
||||
using the `tag` query parameter, as shown in the following curl-based example:
|
||||
|
||||
include::{snippets}metrics/metric-with-tags/curl-request.adoc[]
|
||||
|
||||
The preceding example retrieves the `jvm.memory.max` metric, where the `area` tag has a
|
||||
value of `nonheap` and the `id` attribute has a value of `Code Cache`. The resulting
|
||||
response is similar to the following:
|
||||
|
||||
include::{snippets}metrics/metric-with-tags/http-response.adoc[]
|
@ -0,0 +1,19 @@
|
||||
[[prometheus]]
|
||||
= Prometheus (`prometheus`)
|
||||
|
||||
The `prometheus` endpoint provides Spring Boot application's metrics in the format
|
||||
required for scraping by a Prometheus server.
|
||||
|
||||
|
||||
|
||||
[[prometheus-retrieving]]
|
||||
== Retrieving the Metrics
|
||||
|
||||
To retrieve the metrics, make a `GET` request to `/application/prometheus`, as shown in
|
||||
the following curl-based example:
|
||||
|
||||
include::{snippets}prometheus/curl-request.adoc[]
|
||||
|
||||
The resulting response is similar to the following:
|
||||
|
||||
include::{snippets}prometheus/http-response.adoc[]
|
@ -0,0 +1,30 @@
|
||||
[[scheduled-tasks]]
|
||||
= Scheduled Tasks (`scheduledtasks`)
|
||||
|
||||
The `scheduledtasks` endpoint provides information about the application's scheduled
|
||||
tasks.
|
||||
|
||||
|
||||
|
||||
[[scheduled-tasks-retrieving]]
|
||||
== Retrieving the Scheduled Tasks
|
||||
|
||||
To retrieve the scheduled tasks, make a `GET` request to `/application/scheduledtasks`,
|
||||
as shown in the following curl-based example:
|
||||
|
||||
include::{snippets}scheduled-tasks/curl-request.adoc[]
|
||||
|
||||
The resulting response is similar to the following:
|
||||
|
||||
include::{snippets}scheduled-tasks/http-response.adoc[]
|
||||
|
||||
|
||||
|
||||
[[scheduled-tasks-retrieving-response-structure]]
|
||||
=== Response Structure
|
||||
|
||||
The response contains details of the application's scheduled tasks. The following table
|
||||
describes the structure of the response:
|
||||
|
||||
[cols="2,1,3"]
|
||||
include::{snippets}scheduled-tasks/response-fields.adoc[]
|
@ -0,0 +1,84 @@
|
||||
[[sessions]]
|
||||
= Sessions (`sessions`)
|
||||
|
||||
The `sessions` endpoint provides information about the application's HTTP sessions that
|
||||
are managed by Spring Session.
|
||||
|
||||
|
||||
|
||||
[[sessions-retrieving]]
|
||||
== Retrieving Sessions
|
||||
|
||||
To retrieve the sessions, make a `GET` request to `/application/sessions`, as shown in the
|
||||
following curl-based example:
|
||||
|
||||
include::{snippets}sessions/username/curl-request.adoc[]
|
||||
|
||||
The preceding examples retrieves all of the sessions for the user whose username is
|
||||
`alice`.
|
||||
|
||||
The resulting response is similar to the following:
|
||||
|
||||
include::{snippets}sessions/username/http-response.adoc[]
|
||||
|
||||
|
||||
|
||||
[[sessions-retrieving-query-parameters]]
|
||||
=== Query Parameters
|
||||
|
||||
The endpoint uses query parameters to limit the sessions that it returns. The following
|
||||
table shows the single required query parameter:
|
||||
|
||||
[cols="2,4"]
|
||||
include::{snippets}sessions/username/request-parameters.adoc[]
|
||||
|
||||
|
||||
|
||||
[[sessions-retrieving-response-structure]]
|
||||
=== Response Structure
|
||||
|
||||
The response contains details of the matching sessions. The following table describes the
|
||||
structure of the response:
|
||||
|
||||
[cols="3,1,3"]
|
||||
include::{snippets}sessions/username/response-fields.adoc[]
|
||||
|
||||
|
||||
|
||||
[[sessions-retrieving-id]]
|
||||
== Retrieving a Single Session
|
||||
|
||||
To retrieve a single session, make a `GET` request to `/application/sessions/{id}`, as
|
||||
shown in the following curl-based example:
|
||||
|
||||
include::{snippets}sessions/id/curl-request.adoc[]
|
||||
|
||||
The preceding example retrieves the session with the `id` of
|
||||
`4db5efcc-99cb-4d05-a52c-b49acfbb7ea9`. The resulting response is similar to the
|
||||
following:
|
||||
|
||||
include::{snippets}sessions/id/http-response.adoc[]
|
||||
|
||||
|
||||
|
||||
[[sessions-retrieving-id-response-structure]]
|
||||
=== Response Structure
|
||||
|
||||
The response contains details of the requested session. The following table describes the
|
||||
structure of the response:
|
||||
|
||||
[cols="3,1,3"]
|
||||
include::{snippets}sessions/id/response-fields.adoc[]
|
||||
|
||||
|
||||
|
||||
[[sessions-deleting]]
|
||||
== Deleting a Session
|
||||
|
||||
To delete a session, make a `DELETE` request to `/application/sessions/{id}`, as shown in
|
||||
the following curl-based example:
|
||||
|
||||
include::{snippets}sessions/delete/curl-request.adoc[]
|
||||
|
||||
The preceding example deletes the session with the `id` of
|
||||
`4db5efcc-99cb-4d05-a52c-b49acfbb7ea9`.
|
@ -0,0 +1,29 @@
|
||||
[[shutdown]]
|
||||
= Shutdown (`shutdown`)
|
||||
|
||||
The `shutdown` endpoint is used to shut down the application.
|
||||
|
||||
|
||||
|
||||
[[shutdown-shutting-down]]
|
||||
== Shutting Down the Application
|
||||
|
||||
To shut down the application, make a `POST` request to `/application/shutdown`, as shown
|
||||
in the following curl-based example:
|
||||
|
||||
include::{snippets}shutdown/curl-request.adoc[]
|
||||
|
||||
A response similar to the following is produced:
|
||||
|
||||
include::{snippets}shutdown/http-response.adoc[]
|
||||
|
||||
|
||||
|
||||
[[shutdowm-shutting-down-response-structure]]
|
||||
=== Response Structure
|
||||
|
||||
The response contains details of the result of the shutdown request. The following table
|
||||
describes the structure of the response:
|
||||
|
||||
[cols="3,1,3"]
|
||||
include::{snippets}shutdown/response-fields.adoc[]
|
@ -0,0 +1,29 @@
|
||||
[[status]]
|
||||
= Status (`status`)
|
||||
|
||||
The `status` endpoint provides an overview of the status of the application.
|
||||
|
||||
|
||||
|
||||
[[status-retrieving]]
|
||||
== Retrieving the Status
|
||||
|
||||
To retrieve the status of the application, make a `GET` request to `/application/status`,
|
||||
as shown in the following curl-based example:
|
||||
|
||||
include::{snippets}status/curl-request.adoc[]
|
||||
|
||||
The resulting response is similar to the following:
|
||||
|
||||
include::{snippets}status/http-response.adoc[]
|
||||
|
||||
|
||||
|
||||
[[status-retrieving-response-structure]]
|
||||
=== Response Structure
|
||||
|
||||
The response contains the status of the application. The following table describes the
|
||||
structure of the response:
|
||||
|
||||
[cols="2,1,3"]
|
||||
include::{snippets}status/response-fields.adoc[]
|
@ -0,0 +1,29 @@
|
||||
[[threaddump]]
|
||||
= Thread Dump (`threaddump`)
|
||||
|
||||
The `threaddump` endpoint provides a thread dump from the application's JVM.
|
||||
|
||||
|
||||
|
||||
[[threaddump-retrieving]]
|
||||
== Retrieving the Thread Dump
|
||||
|
||||
To retrieve the thread dump, make a `GET` request to `/application/threaddump`, as shown
|
||||
in the following curl-based example:
|
||||
|
||||
include::{snippets}threaddump/curl-request.adoc[]
|
||||
|
||||
The resulting response is similar to the following:
|
||||
|
||||
include::{snippets}threaddump/http-response.adoc[]
|
||||
|
||||
|
||||
|
||||
[[threaddump-retrieving-response-structure]]
|
||||
=== Response Structure
|
||||
|
||||
The response contains details of the JVM's threads. The following table describes the
|
||||
structure of the response:
|
||||
|
||||
[cols="3,1,2"]
|
||||
include::{snippets}threaddump/response-fields.adoc[]
|
@ -0,0 +1,70 @@
|
||||
= Spring Boot Actuator Web API Documentation
|
||||
Andy Wilkinson
|
||||
:doctype: book
|
||||
:toc: left
|
||||
:toclevels: 4
|
||||
:source-highlighter: prettify
|
||||
:numbered:
|
||||
:icons: font
|
||||
:hide-uri-scheme:
|
||||
|
||||
This API documentation describes Spring Boot Actuators web endpoints.
|
||||
|
||||
|
||||
|
||||
[[overview]]
|
||||
== Overview
|
||||
|
||||
Before you proceed, you should read the following topics:
|
||||
|
||||
* <<overview-endpoint-urls>>
|
||||
* <<overview-timestamps>>
|
||||
|
||||
|
||||
|
||||
[[overview-endpoint-urls]]
|
||||
=== URLs
|
||||
|
||||
By default, all web endpoints are available beneath the path `/application` with URLs of
|
||||
the form `/application/{id}`. The `/application` base path can be configured by using the
|
||||
`management.endpoints.web.base-path` property, as shown in the following example:
|
||||
|
||||
[source,properties,indent=0]
|
||||
----
|
||||
management.endpoints.web.base-path=/manage
|
||||
----
|
||||
|
||||
The preceding `application.properties` example changes the form of the endpoint URLs from
|
||||
`/application/{id}` to `/manage/{id}`. For example, the URL `info` endpoint would become
|
||||
`/manage/info`.
|
||||
|
||||
|
||||
|
||||
[[overview-timestamps]]
|
||||
=== Timestamps
|
||||
|
||||
All timestamps that are consumed by the endpoints, either as query parameters or in the
|
||||
request body, must be formatted as an offset date and time as specified in
|
||||
https://en.wikipedia.org/wiki/ISO_8601[ISO 8601].
|
||||
|
||||
|
||||
|
||||
include::endpoints/auditevents.adoc[leveloffset=+1]
|
||||
include::endpoints/beans.adoc[leveloffset=+1]
|
||||
include::endpoints/conditions.adoc[leveloffset=+1]
|
||||
include::endpoints/configprops.adoc[leveloffset=+1]
|
||||
include::endpoints/env.adoc[leveloffset=+1]
|
||||
include::endpoints/flyway.adoc[leveloffset=+1]
|
||||
include::endpoints/health.adoc[leveloffset=+1]
|
||||
include::endpoints/heapdump.adoc[leveloffset=+1]
|
||||
include::endpoints/info.adoc[leveloffset=+1]
|
||||
include::endpoints/liquibase.adoc[leveloffset=+1]
|
||||
include::endpoints/logfile.adoc[leveloffset=+1]
|
||||
include::endpoints/loggers.adoc[leveloffset=+1]
|
||||
include::endpoints/metrics.adoc[leveloffset=+1]
|
||||
include::endpoints/prometheus.adoc[leveloffset=+1]
|
||||
include::endpoints/scheduledtasks.adoc[leveloffset=+1]
|
||||
include::endpoints/sessions.adoc[leveloffset=+1]
|
||||
include::endpoints/shutdown.adoc[leveloffset=+1]
|
||||
include::endpoints/status.adoc[leveloffset=+1]
|
||||
include::endpoints/threaddump.adoc[leveloffset=+1]
|
@ -0,0 +1,142 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.autoconfigure.endpoint.web.documentation;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||
import org.flywaydb.core.internal.util.StringUtils;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.web.servlet.WebMvcEndpointManagementContextConfiguration;
|
||||
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.restdocs.JUnitRestDocumentation;
|
||||
import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation;
|
||||
import org.springframework.restdocs.operation.preprocess.ContentModifyingOperationPreprocessor;
|
||||
import org.springframework.restdocs.operation.preprocess.OperationPreprocessor;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
|
||||
/**
|
||||
* Abstract base class for tests that generate endpoint documentation using Spring REST
|
||||
* Docs.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(properties = { "spring.jackson.serialization.indent_output=true",
|
||||
"management.endpoints.web.expose=*" })
|
||||
public abstract class AbstractEndpointDocumentationTests {
|
||||
|
||||
@Rule
|
||||
public final JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation();
|
||||
|
||||
protected MockMvc mockMvc;
|
||||
|
||||
@Autowired
|
||||
private WebApplicationContext applicationContext;
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.applicationContext)
|
||||
.apply(MockMvcRestDocumentation
|
||||
.documentationConfiguration(this.restDocumentation).uris())
|
||||
.build();
|
||||
}
|
||||
|
||||
protected String describeEnumValues(Class<? extends Enum<?>> enumType) {
|
||||
return StringUtils
|
||||
.collectionToCommaDelimitedString(Stream.of(enumType.getEnumConstants())
|
||||
.map((constant) -> "`" + constant.name() + "`")
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
protected OperationPreprocessor limit(String key) {
|
||||
return limit(key, (candidate) -> true);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected <T> OperationPreprocessor limit(String key, Predicate<T> filter) {
|
||||
return new ContentModifyingOperationPreprocessor((content, mediaType) -> {
|
||||
ObjectMapper objectMapper = new ObjectMapper()
|
||||
.enable(SerializationFeature.INDENT_OUTPUT);
|
||||
try {
|
||||
Map<String, Object> payload = objectMapper.readValue(content, Map.class);
|
||||
Object entry = payload.get(key);
|
||||
if (entry instanceof Map) {
|
||||
payload.put(key, select((Map<String, Object>) entry, filter));
|
||||
}
|
||||
else {
|
||||
payload.put(key, select((List<Object>) entry, filter));
|
||||
}
|
||||
return objectMapper.writeValueAsBytes(payload);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> Map<String, Object> select(Map<String, Object> candidates,
|
||||
Predicate<T> filter) {
|
||||
Map<String, Object> selected = new HashMap<String, Object>();
|
||||
candidates.entrySet().stream().filter((candidate) -> filter.test((T) candidate))
|
||||
.limit(3)
|
||||
.forEach((entry) -> selected.put(entry.getKey(), entry.getValue()));
|
||||
return selected;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> List<Object> select(List<Object> candidates, Predicate<T> filter) {
|
||||
return candidates.stream().filter((candidate) -> filter.test((T) candidate))
|
||||
.limit(3).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import({ JacksonAutoConfiguration.class,
|
||||
HttpMessageConvertersAutoConfiguration.class, WebMvcAutoConfiguration.class,
|
||||
DispatcherServletAutoConfiguration.class, EndpointAutoConfiguration.class,
|
||||
WebEndpointAutoConfiguration.class,
|
||||
WebMvcEndpointManagementContextConfiguration.class,
|
||||
PropertyPlaceholderAutoConfiguration.class })
|
||||
static class BaseDocumentationConfiguration {
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.autoconfigure.endpoint.web.documentation;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.boot.actuate.audit.AuditEvent;
|
||||
import org.springframework.boot.actuate.audit.AuditEventRepository;
|
||||
import org.springframework.boot.actuate.audit.AuditEventsEndpoint;
|
||||
import org.springframework.boot.actuate.audit.AuditEventsEndpointWebExtension;
|
||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
|
||||
import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName;
|
||||
import static org.springframework.restdocs.request.RequestDocumentation.requestParameters;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
/**
|
||||
* Tests for generating documentation describing {@link AuditEventsEndpoint}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class AuditEventsEndpointDocumentationTests
|
||||
extends AbstractEndpointDocumentationTests {
|
||||
|
||||
@MockBean
|
||||
private AuditEventRepository repository;
|
||||
|
||||
@Test
|
||||
public void allAuditEventsAfter() throws Exception {
|
||||
String queryTimestamp = "2017-11-07T09:37Z";
|
||||
given(this.repository.find(any(), any(), any())).willReturn(
|
||||
Arrays.asList(new AuditEvent("alice", "logout", Collections.emptyMap())));
|
||||
this.mockMvc
|
||||
.perform(get("/application/auditevents").param("after", queryTimestamp))
|
||||
.andExpect(status().isOk())
|
||||
.andDo(document("auditevents/after", responseFields(
|
||||
fieldWithPath("events").description("An array of audit events."),
|
||||
fieldWithPath("events.[].timestamp")
|
||||
.description("The timestamp of when the event occurred."),
|
||||
fieldWithPath("events.[].principal")
|
||||
.description("The principal that triggered the event."),
|
||||
fieldWithPath("events.[].type")
|
||||
.description("The type of the event."))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void filteredAuditEvents() throws Exception {
|
||||
ZonedDateTime now = ZonedDateTime.now();
|
||||
String queryTimestamp = DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(now);
|
||||
Date date = new Date(now.toEpochSecond() * 1000);
|
||||
given(this.repository.find("alice", date, "logout")).willReturn(
|
||||
Arrays.asList(new AuditEvent("alice", "logout", Collections.emptyMap())));
|
||||
this.mockMvc
|
||||
.perform(get("/application/auditevents").param("principal", "alice")
|
||||
.param("after", queryTimestamp).param("type", "logout"))
|
||||
.andExpect(status().isOk())
|
||||
.andDo(document("auditevents/filtered",
|
||||
requestParameters(
|
||||
parameterWithName("after").description(
|
||||
"Restricts the events to those that occurred "
|
||||
+ "after the given time. Required."),
|
||||
parameterWithName("principal").description(
|
||||
"Restricts the events to those with the given "
|
||||
+ "principal. Optional."),
|
||||
parameterWithName("type").description(
|
||||
"Restricts the events to those with the given "
|
||||
+ "type. Optional."))));
|
||||
verify(this.repository).find("alice", date, "logout");
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import(BaseDocumentationConfiguration.class)
|
||||
static class TestConfiguration {
|
||||
|
||||
@Bean
|
||||
public AuditEventsEndpoint auditEventsEndpoint(AuditEventRepository repository) {
|
||||
return new AuditEventsEndpoint(repository);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AuditEventsEndpointWebExtension adAuditEventsWebEndpointExtension(
|
||||
AuditEventsEndpoint delegate) {
|
||||
return new AuditEventsEndpointWebExtension(delegate);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.autoconfigure.endpoint.web.documentation;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.boot.actuate.beans.BeansEndpoint;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.restdocs.payload.FieldDescriptor;
|
||||
import org.springframework.restdocs.payload.JsonFieldType;
|
||||
import org.springframework.restdocs.payload.ResponseFieldsSnippet;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
|
||||
import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse;
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.subsectionWithPath;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
/**
|
||||
* Tests for generating documentation describing {@link BeansEndpoint}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class BeansEndpointDocumentationTests extends AbstractEndpointDocumentationTests {
|
||||
|
||||
@Test
|
||||
public void beans() throws Exception {
|
||||
List<FieldDescriptor> beanFields = Arrays
|
||||
.asList(fieldWithPath("aliases").description("Names of any aliases."),
|
||||
fieldWithPath("scope")
|
||||
.description("Scope of the bean."),
|
||||
fieldWithPath("type").description("Fully qualified type of the bean."),
|
||||
fieldWithPath("resource")
|
||||
.description("Resource in which the bean was defined, if any.")
|
||||
.optional(),
|
||||
fieldWithPath("dependencies").description("Names of any dependencies."));
|
||||
ResponseFieldsSnippet responseFields = responseFields(
|
||||
fieldWithPath("contextId").description("ID of the application context."),
|
||||
fieldWithPath("beans.*")
|
||||
.description("Beans in the application context keyed by name."))
|
||||
.andWithPrefix("beans.*.", beanFields)
|
||||
.and(subsectionWithPath("parent")
|
||||
.description("Beans in the parent application "
|
||||
+ "context, if any.")
|
||||
.type(JsonFieldType.OBJECT).optional());
|
||||
this.mockMvc.perform(get("/application/beans")).andExpect(status().isOk())
|
||||
.andDo(document("beans",
|
||||
preprocessResponse(limit("beans", this::isIndependentBean)),
|
||||
responseFields));
|
||||
}
|
||||
|
||||
private boolean isIndependentBean(Entry<String, Map<String, Object>> bean) {
|
||||
return CollectionUtils.isEmpty((Collection<?>) bean.getValue().get("aliases"))
|
||||
&& CollectionUtils
|
||||
.isEmpty((Collection<?>) bean.getValue().get("dependencies"));
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import(BaseDocumentationConfiguration.class)
|
||||
static class TestConfiguration {
|
||||
|
||||
@Bean
|
||||
public BeansEndpoint beansEndpoint(ConfigurableApplicationContext context) {
|
||||
return new BeansEndpoint(context);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.autoconfigure.endpoint.web.documentation;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.boot.actuate.autoconfigure.condition.ConditionsReportEndpoint;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionEvaluationReport;
|
||||
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.restdocs.JUnitRestDocumentation;
|
||||
import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation;
|
||||
import org.springframework.restdocs.payload.JsonFieldType;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
|
||||
import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse;
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
/**
|
||||
* Tests for generating documentation describing {@link ConditionsReportEndpoint}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class ConditionsReportEndpointDocumentationTests
|
||||
extends AbstractEndpointDocumentationTests {
|
||||
|
||||
@Rule
|
||||
public final JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation();
|
||||
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@Autowired
|
||||
private WebApplicationContext applicationContext;
|
||||
|
||||
@Override
|
||||
@Before
|
||||
public void before() {
|
||||
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.applicationContext)
|
||||
.apply(MockMvcRestDocumentation
|
||||
.documentationConfiguration(this.restDocumentation).uris())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void conditions() throws Exception {
|
||||
this.mockMvc.perform(get("/application/conditions")).andExpect(status().isOk())
|
||||
.andDo(MockMvcRestDocumentation.document("conditions",
|
||||
preprocessResponse(limit("positiveMatches"),
|
||||
limit("negativeMatches")),
|
||||
responseFields(
|
||||
fieldWithPath("positiveMatches").description(
|
||||
"Classes and methods with conditions that were matched."),
|
||||
fieldWithPath("positiveMatches.*.[].condition")
|
||||
.description("Name of the condition."),
|
||||
fieldWithPath("positiveMatches.*.[].message").description(
|
||||
"Details of why the condition was matched."),
|
||||
fieldWithPath("negativeMatches").description(
|
||||
"Classes and methods with conditions that were not matched."),
|
||||
fieldWithPath("negativeMatches.*.notMatched")
|
||||
.description("Conditions that were matched."),
|
||||
fieldWithPath("negativeMatches.*.notMatched.[].condition")
|
||||
.description("Name of the condition."),
|
||||
fieldWithPath("negativeMatches.*.notMatched.[].message")
|
||||
.description(
|
||||
"Details of why the condition was not matched."),
|
||||
fieldWithPath("negativeMatches.*.matched")
|
||||
.description("Conditions that were matched."),
|
||||
fieldWithPath("negativeMatches.*.matched.[].condition")
|
||||
.description("Name of the condition.")
|
||||
.type(JsonFieldType.STRING).optional(),
|
||||
fieldWithPath("negativeMatches.*.matched.[].message")
|
||||
.description(
|
||||
"Details of why the condition was matched.")
|
||||
.type(JsonFieldType.STRING).optional(),
|
||||
fieldWithPath("unconditionalClasses").description(
|
||||
"Names of unconditional auto-configuration classes, if any."))));
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import(BaseDocumentationConfiguration.class)
|
||||
static class TestConfiguration {
|
||||
|
||||
@Bean
|
||||
public ConditionsReportEndpoint autoConfigurationReportEndpoint(
|
||||
ConfigurableListableBeanFactory beanFactory) {
|
||||
ConditionEvaluationReport conditionEvaluationReport = ConditionEvaluationReport
|
||||
.get(beanFactory);
|
||||
conditionEvaluationReport.recordEvaluationCandidates(
|
||||
Arrays.asList(PropertyPlaceholderAutoConfiguration.class.getName()));
|
||||
return new ConditionsReportEndpoint(conditionEvaluationReport);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.autoconfigure.endpoint.web.documentation;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.boot.actuate.context.properties.ConfigurationPropertiesReportEndpoint;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation;
|
||||
import org.springframework.restdocs.payload.JsonFieldType;
|
||||
|
||||
import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse;
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.subsectionWithPath;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
/**
|
||||
* Tests for generating documentation describing
|
||||
* {@link ConfigurationPropertiesReportEndpoint}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class ConfigurationPropertiesReportEndpointDocumentationTests
|
||||
extends AbstractEndpointDocumentationTests {
|
||||
|
||||
@Test
|
||||
public void configProps() throws Exception {
|
||||
this.mockMvc.perform(get("/application/configprops")).andExpect(status().isOk())
|
||||
.andDo(MockMvcRestDocumentation.document("configprops",
|
||||
preprocessResponse(limit("beans")),
|
||||
responseFields(
|
||||
fieldWithPath("contextId")
|
||||
.description("ID of the application context."),
|
||||
fieldWithPath("beans.*").description(
|
||||
"`@ConfigurationProperties` beans keyed by bean name."),
|
||||
fieldWithPath("beans.*.prefix").description(
|
||||
"Prefix applied to the names of the bean's properties."),
|
||||
subsectionWithPath("beans.*.properties").description(
|
||||
"Properties of the bean as name-value pairs."),
|
||||
subsectionWithPath("parent")
|
||||
.description(
|
||||
"`@ConfigurationProperties` beans in the parent "
|
||||
+ "context, if any.")
|
||||
.optional().type(JsonFieldType.OBJECT))));
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import(BaseDocumentationConfiguration.class)
|
||||
static class TestConfiguration {
|
||||
|
||||
@Bean
|
||||
public ConfigurationPropertiesReportEndpoint endpoint() {
|
||||
return new ConfigurationPropertiesReportEndpoint();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,179 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.autoconfigure.endpoint.web.documentation;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.boot.actuate.env.EnvironmentEndpoint;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.core.env.AbstractEnvironment;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.EnumerablePropertySource;
|
||||
import org.springframework.core.env.MutablePropertySources;
|
||||
import org.springframework.core.env.PropertySource;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.restdocs.operation.preprocess.ContentModifyingOperationPreprocessor;
|
||||
import org.springframework.restdocs.operation.preprocess.OperationPreprocessor;
|
||||
import org.springframework.restdocs.payload.FieldDescriptor;
|
||||
import org.springframework.test.context.TestPropertySource;
|
||||
|
||||
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
|
||||
import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse;
|
||||
import static org.springframework.restdocs.operation.preprocess.Preprocessors.replacePattern;
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
/**
|
||||
* Tests for generating documentation describing the {@link EnvironmentEndpoint}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
@TestPropertySource(properties = "spring.config.location=classpath:/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/")
|
||||
public class EnvironmentEndpointDocumentationTests
|
||||
extends AbstractEndpointDocumentationTests {
|
||||
|
||||
private static final FieldDescriptor activeProfiles = fieldWithPath("activeProfiles")
|
||||
.description("Names of the active profiles, if any.");
|
||||
|
||||
private static final FieldDescriptor propertySources = fieldWithPath(
|
||||
"propertySources").description("Property sources in order of precedence.");
|
||||
|
||||
private static final FieldDescriptor propertySourceName = fieldWithPath(
|
||||
"propertySources.[].name").description("Name of the property source.");
|
||||
|
||||
@Test
|
||||
public void env() throws Exception {
|
||||
this.mockMvc.perform(get("/application/env")).andExpect(status().isOk())
|
||||
.andDo(document("env/all",
|
||||
preprocessResponse(replacePattern(
|
||||
Pattern.compile(
|
||||
"org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/"),
|
||||
""), filterProperties()),
|
||||
responseFields(activeProfiles, propertySources,
|
||||
propertySourceName,
|
||||
fieldWithPath("propertySources.[].properties")
|
||||
.description(
|
||||
"Properties in the property source keyed by property name."),
|
||||
fieldWithPath("propertySources.[].properties.*.value")
|
||||
.description("Value of the property."),
|
||||
fieldWithPath("propertySources.[].properties.*.origin")
|
||||
.description("Origin of the property, if any.")
|
||||
.optional())));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void singlePropertyFromEnv() throws Exception {
|
||||
this.mockMvc.perform(get("/application/env/com.example.cache.max-size"))
|
||||
.andExpect(status().isOk())
|
||||
.andDo(document("env/single",
|
||||
preprocessResponse(replacePattern(
|
||||
Pattern.compile(
|
||||
"org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/"),
|
||||
"")),
|
||||
responseFields(
|
||||
fieldWithPath("property").description(
|
||||
"Property from the environment, if found.")
|
||||
.optional(),
|
||||
fieldWithPath("property.source").description(
|
||||
"Name of the source of the property."),
|
||||
fieldWithPath("property.value")
|
||||
.description("Value of the property."),
|
||||
activeProfiles, propertySources, propertySourceName,
|
||||
fieldWithPath("propertySources.[].property").description(
|
||||
"Property in the property source, if any.")
|
||||
.optional(),
|
||||
fieldWithPath("propertySources.[].property.value")
|
||||
.description("Value of the property."),
|
||||
fieldWithPath("propertySources.[].property.origin")
|
||||
.description("Origin of the property, if any.")
|
||||
.optional())));
|
||||
}
|
||||
|
||||
private OperationPreprocessor filterProperties() {
|
||||
return new ContentModifyingOperationPreprocessor(this::filterProperties);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private byte[] filterProperties(byte[] content, MediaType mediaType) {
|
||||
ObjectMapper objectMapper = new ObjectMapper()
|
||||
.enable(SerializationFeature.INDENT_OUTPUT);
|
||||
try {
|
||||
Map<String, Object> payload = objectMapper.readValue(content, Map.class);
|
||||
List<Map<String, Object>> propertySources = (List<Map<String, Object>>) payload
|
||||
.get("propertySources");
|
||||
for (Map<String, Object> propertySource : propertySources) {
|
||||
Map<String, String> properties = (Map<String, String>) propertySource
|
||||
.get("properties");
|
||||
Set<String> filteredKeys = properties.keySet().stream()
|
||||
.filter(this::retainKey).limit(3).collect(Collectors.toSet());
|
||||
properties.keySet().retainAll(filteredKeys);
|
||||
}
|
||||
return objectMapper.writeValueAsBytes(payload);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean retainKey(String key) {
|
||||
return key.startsWith("java.") || key.equals("JAVA_HOME")
|
||||
|| key.startsWith("com.example");
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import(BaseDocumentationConfiguration.class)
|
||||
static class TestConfiguration {
|
||||
|
||||
@Bean
|
||||
public EnvironmentEndpoint endpoint(ConfigurableEnvironment environment) {
|
||||
return new EnvironmentEndpoint(new AbstractEnvironment() {
|
||||
|
||||
@Override
|
||||
protected void customizePropertySources(
|
||||
MutablePropertySources propertySources) {
|
||||
StreamSupport
|
||||
.stream(environment.getPropertySources().spliterator(), false)
|
||||
.filter(this::includedPropertySource)
|
||||
.forEach(propertySources::addLast);
|
||||
}
|
||||
|
||||
private boolean includedPropertySource(PropertySource<?> propertySource) {
|
||||
return propertySource instanceof EnumerablePropertySource
|
||||
&& !"Inlined Test Properties"
|
||||
.equals(propertySource.getName());
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.autoconfigure.endpoint.web.documentation;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.flywaydb.core.Flyway;
|
||||
import org.flywaydb.core.api.MigrationState;
|
||||
import org.flywaydb.core.api.MigrationType;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.boot.actuate.flyway.FlywayEndpoint;
|
||||
import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration;
|
||||
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation;
|
||||
import org.springframework.restdocs.payload.FieldDescriptor;
|
||||
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
/**
|
||||
* Tests for generating documentation describing the {@link FlywayEndpoint}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
@AutoConfigureTestDatabase
|
||||
public class FlywayEndpointDocumentationTests extends AbstractEndpointDocumentationTests {
|
||||
|
||||
@Test
|
||||
public void flyway() throws Exception {
|
||||
this.mockMvc.perform(get("/application/flyway")).andExpect(status().isOk())
|
||||
.andDo(MockMvcRestDocumentation.document("flyway",
|
||||
responseFields(fieldWithPath("*.migrations").description(
|
||||
"Migrations performed by the Flyway instance, keyed by"
|
||||
+ " bean name.")).andWithPrefix(
|
||||
"*.migrations.[].",
|
||||
migrationFieldDescriptors())));
|
||||
}
|
||||
|
||||
private List<FieldDescriptor> migrationFieldDescriptors() {
|
||||
return Arrays.asList(
|
||||
fieldWithPath("checksum")
|
||||
.description("Checksum of the migration, if any.").optional(),
|
||||
fieldWithPath("description")
|
||||
.description("Description of the migration, if any.").optional(),
|
||||
fieldWithPath("executionTime")
|
||||
.description(
|
||||
"Execution time in milliseconds of an applied migration.")
|
||||
.optional(),
|
||||
fieldWithPath("installedBy")
|
||||
.description("User that installed the applied migration, if any.")
|
||||
.optional(),
|
||||
fieldWithPath("installedOn").description(
|
||||
"Timestamp of when the applied migration was installed, "
|
||||
+ "if any.")
|
||||
.optional(),
|
||||
fieldWithPath("installedRank").description(
|
||||
"Rank of the applied migration, if any. Later migrations have "
|
||||
+ "higher ranks.")
|
||||
.optional(),
|
||||
fieldWithPath("script")
|
||||
.description(
|
||||
"Name of the script used to execute the migration, if any.")
|
||||
.optional(),
|
||||
fieldWithPath("state").description("State of the migration. ("
|
||||
+ describeEnumValues(MigrationState.class) + ")"),
|
||||
fieldWithPath("type").description("Type of the migration. ("
|
||||
+ describeEnumValues(MigrationType.class) + ")"),
|
||||
fieldWithPath("version").description(
|
||||
"Version of the database after applying the migration, "
|
||||
+ "if any.")
|
||||
.optional());
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import({ BaseDocumentationConfiguration.class, EmbeddedDataSourceConfiguration.class,
|
||||
FlywayAutoConfiguration.class })
|
||||
static class TestConfiguration {
|
||||
|
||||
@Bean
|
||||
public FlywayEndpoint endpoint(Map<String, Flyway> flyways) {
|
||||
return new FlywayEndpoint(flyways);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.autoconfigure.endpoint.web.documentation;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.boot.actuate.health.CompositeHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.HealthEndpoint;
|
||||
import org.springframework.boot.actuate.health.HealthIndicator;
|
||||
import org.springframework.boot.actuate.health.OrderedHealthAggregator;
|
||||
import org.springframework.boot.actuate.jdbc.DataSourceHealthIndicator;
|
||||
import org.springframework.boot.actuate.system.DiskSpaceHealthIndicator;
|
||||
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.subsectionWithPath;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
/**
|
||||
* Tests for generating documentation describing the {@link HealthEndpoint}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class HealthEndpointDocumentationTests extends AbstractEndpointDocumentationTests {
|
||||
|
||||
@Test
|
||||
public void health() throws Exception {
|
||||
this.mockMvc.perform(get("/application/health")).andExpect(status().isOk())
|
||||
.andDo(document("health",
|
||||
responseFields(
|
||||
fieldWithPath("status").description(
|
||||
"Overall status of the application."),
|
||||
fieldWithPath("details")
|
||||
.description("Details of the health of the application."),
|
||||
fieldWithPath("details.*.status").description(
|
||||
"Status of a specific part of the application."),
|
||||
subsectionWithPath("details.*.details").description(
|
||||
"Details of the health of a specific part of the"
|
||||
+ " application."))));
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import(BaseDocumentationConfiguration.class)
|
||||
@ImportAutoConfiguration(DataSourceAutoConfiguration.class)
|
||||
static class TestConfiguration {
|
||||
|
||||
@Bean
|
||||
public HealthEndpoint endpoint(Map<String, HealthIndicator> healthIndicators) {
|
||||
return new HealthEndpoint(new CompositeHealthIndicator(
|
||||
new OrderedHealthAggregator(), healthIndicators));
|
||||
}
|
||||
|
||||
@Bean
|
||||
public DiskSpaceHealthIndicator diskSpaceHealthIndicator() {
|
||||
return new DiskSpaceHealthIndicator(new File("."), 1024 * 1024 * 10);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public DataSourceHealthIndicator dataSourceHealthIndicator(
|
||||
DataSource dataSource) {
|
||||
return new DataSourceHealthIndicator(dataSource);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.autoconfigure.endpoint.web.documentation;
|
||||
|
||||
import java.io.FileWriter;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.boot.actuate.management.HeapDumpWebEndpoint;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.restdocs.cli.CliDocumentation;
|
||||
import org.springframework.restdocs.cli.CurlRequestSnippet;
|
||||
import org.springframework.restdocs.operation.Operation;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
|
||||
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
/**
|
||||
* Tests for generating documentation describing the {@link HeapDumpWebEndpoint}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class HeapDumpWebEndpointDocumentationTests
|
||||
extends AbstractEndpointDocumentationTests {
|
||||
|
||||
@Test
|
||||
public void heapDump() throws Exception {
|
||||
this.mockMvc.perform(get("/application/heapdump")).andExpect(status().isOk())
|
||||
.andDo(document("heapdump",
|
||||
new CurlRequestSnippet(CliDocumentation.multiLineFormat()) {
|
||||
|
||||
@Override
|
||||
protected Map<String, Object> createModel(
|
||||
Operation operation) {
|
||||
Map<String, Object> model = super.createModel(operation);
|
||||
model.put("options", "-O");
|
||||
return model;
|
||||
}
|
||||
|
||||
}));
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import(BaseDocumentationConfiguration.class)
|
||||
static class TestConfiguration {
|
||||
|
||||
@Bean
|
||||
public HeapDumpWebEndpoint endpoint() {
|
||||
return new HeapDumpWebEndpoint() {
|
||||
|
||||
@Override
|
||||
protected HeapDumper createHeapDumper()
|
||||
throws HeapDumperUnavailableException {
|
||||
return (file, live) -> FileCopyUtils.copy("<<binary content>>",
|
||||
new FileWriter(file));
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.autoconfigure.endpoint.web.documentation;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.boot.actuate.info.BuildInfoContributor;
|
||||
import org.springframework.boot.actuate.info.GitInfoContributor;
|
||||
import org.springframework.boot.actuate.info.InfoContributor;
|
||||
import org.springframework.boot.actuate.info.InfoEndpoint;
|
||||
import org.springframework.boot.info.BuildProperties;
|
||||
import org.springframework.boot.info.GitProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation;
|
||||
import org.springframework.restdocs.payload.JsonFieldType;
|
||||
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.beneathPath;
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
/**
|
||||
* Tests for generating documentation describing the {@link InfoEndpoint}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class InfoEndpointDocumentationTests extends AbstractEndpointDocumentationTests {
|
||||
|
||||
@Test
|
||||
public void info() throws Exception {
|
||||
this.mockMvc.perform(get("/application/info")).andExpect(status().isOk())
|
||||
.andDo(MockMvcRestDocumentation.document("info",
|
||||
responseFields(beneathPath("git"),
|
||||
fieldWithPath("branch")
|
||||
.description("Name of the Git branch, if any."),
|
||||
fieldWithPath("commit")
|
||||
.description("Details of the Git commit, if any."),
|
||||
fieldWithPath("commit.time")
|
||||
.description("Timestamp of the commit, if any.")
|
||||
.type(JsonFieldType.VARIES),
|
||||
fieldWithPath("commit.id")
|
||||
.description("ID of the commit, if any.")),
|
||||
responseFields(beneathPath("build"),
|
||||
fieldWithPath("artifact")
|
||||
.description(
|
||||
"Artifact ID of the application, if any.")
|
||||
.optional(),
|
||||
fieldWithPath("group")
|
||||
.description(
|
||||
"Group ID of the application, if any.")
|
||||
.optional(),
|
||||
fieldWithPath("name")
|
||||
.description("Name of the application, if any.")
|
||||
.type(JsonFieldType.STRING).optional(),
|
||||
fieldWithPath("version")
|
||||
.description(
|
||||
"Version of the application, if any.")
|
||||
.optional(),
|
||||
fieldWithPath("time")
|
||||
.description(
|
||||
"Timestamp of when the application was built, if any.")
|
||||
.type(JsonFieldType.VARIES).optional())));
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import(BaseDocumentationConfiguration.class)
|
||||
static class TestConfiguration {
|
||||
|
||||
@Bean
|
||||
public InfoEndpoint endpoint(List<InfoContributor> infoContributors) {
|
||||
return new InfoEndpoint(infoContributors);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public GitInfoContributor gitInfoContributor() {
|
||||
Properties properties = new Properties();
|
||||
properties.put("branch", "master");
|
||||
properties.put("commit.id", "df027cf1ec5aeba2d4fedd7b8c42b88dc5ce38e5");
|
||||
properties.put("commit.id.abbrev", "df027cf");
|
||||
properties.put("commit.time", Long.toString(System.currentTimeMillis()));
|
||||
GitProperties gitProperties = new GitProperties(properties);
|
||||
return new GitInfoContributor(gitProperties);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public BuildInfoContributor buildInfoContributor() {
|
||||
Properties properties = new Properties();
|
||||
properties.put("group", "com.example");
|
||||
properties.put("artifact", "application");
|
||||
properties.put("version", "1.0.3");
|
||||
BuildProperties buildProperties = new BuildProperties(properties);
|
||||
return new BuildInfoContributor(buildProperties);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.autoconfigure.endpoint.web.documentation;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import liquibase.changelog.ChangeSet.ExecType;
|
||||
import liquibase.integration.spring.SpringLiquibase;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.boot.actuate.liquibase.LiquibaseEndpoint;
|
||||
import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration;
|
||||
import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation;
|
||||
import org.springframework.restdocs.payload.FieldDescriptor;
|
||||
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
/**
|
||||
* Tests for generating documentation describing the {@link LiquibaseEndpoint}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class LiquibaseEndpointDocumentationTests
|
||||
extends AbstractEndpointDocumentationTests {
|
||||
|
||||
@Test
|
||||
public void liquibase() throws Exception {
|
||||
this.mockMvc.perform(get("/application/liquibase")).andExpect(status().isOk())
|
||||
.andDo(MockMvcRestDocumentation.document("liquibase",
|
||||
responseFields(fieldWithPath("*.changeSets").description(
|
||||
"Change sets made by the Liquibase beans, keyed by "
|
||||
+ "bean name.")).andWithPrefix("*.changeSets[].",
|
||||
getChangeSetFieldDescriptors())));
|
||||
}
|
||||
|
||||
private List<FieldDescriptor> getChangeSetFieldDescriptors() {
|
||||
return Arrays.asList(
|
||||
fieldWithPath("author").description("Author of the change set."),
|
||||
fieldWithPath("changeLog")
|
||||
.description("Change log that contains the change set."),
|
||||
fieldWithPath("comments").description("Comments on the change set."),
|
||||
fieldWithPath("contexts").description("Contexts of the change set."),
|
||||
fieldWithPath("dateExecuted")
|
||||
.description("Timestamp of when the change set was executed."),
|
||||
fieldWithPath("deploymentId")
|
||||
.description("ID of the deployment that ran the change set."),
|
||||
fieldWithPath("description")
|
||||
.description("Description of the change set."),
|
||||
fieldWithPath("execType").description("Execution type of the change set ("
|
||||
+ describeEnumValues(ExecType.class) + ")."),
|
||||
fieldWithPath("id").description("ID of the change set."),
|
||||
fieldWithPath("labels")
|
||||
.description("Labels associated with the change set."),
|
||||
fieldWithPath("checksum").description("Checksum of the change set."),
|
||||
fieldWithPath("orderExecuted")
|
||||
.description("Order of the execution of the change set."),
|
||||
fieldWithPath("tag").description("Tag associated with the change set."));
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import({ BaseDocumentationConfiguration.class, EmbeddedDataSourceConfiguration.class,
|
||||
LiquibaseAutoConfiguration.class })
|
||||
static class TestConfiguration {
|
||||
|
||||
@Bean
|
||||
public LiquibaseEndpoint endpoint(Map<String, SpringLiquibase> liquibases) {
|
||||
return new LiquibaseEndpoint(liquibases);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.autoconfigure.endpoint.web.documentation;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.boot.actuate.logging.LogFileWebEndpoint;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation;
|
||||
import org.springframework.test.context.TestPropertySource;
|
||||
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
/**
|
||||
* Tests for generating documentation describing the {@link LogFileWebEndpoint}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
@TestPropertySource(properties = "logging.file=src/test/resources/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/sample.log")
|
||||
public class LogFileWebEndpointDocumentationTests
|
||||
extends AbstractEndpointDocumentationTests {
|
||||
|
||||
@Test
|
||||
public void logFile() throws Exception {
|
||||
this.mockMvc.perform(get("/application/logfile")).andExpect(status().isOk())
|
||||
.andDo(MockMvcRestDocumentation.document("logfile/entire"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void logFileRange() throws Exception {
|
||||
this.mockMvc.perform(get("/application/logfile").header("Range", "bytes=0-1023"))
|
||||
.andExpect(status().isPartialContent())
|
||||
.andDo(MockMvcRestDocumentation.document("logfile/range"));
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import(BaseDocumentationConfiguration.class)
|
||||
static class TestConfiguration {
|
||||
|
||||
@Bean
|
||||
public LogFileWebEndpoint endpoint(Environment environment) {
|
||||
return new LogFileWebEndpoint(environment);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.autoconfigure.endpoint.web.documentation;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.boot.actuate.logging.LoggersEndpoint;
|
||||
import org.springframework.boot.logging.LogLevel;
|
||||
import org.springframework.boot.logging.LoggerConfiguration;
|
||||
import org.springframework.boot.logging.LoggingSystem;
|
||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation;
|
||||
import org.springframework.restdocs.payload.FieldDescriptor;
|
||||
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields;
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
/**
|
||||
* Tests for generating documentation describing the {@link LoggersEndpoint}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class LoggersEndpointDocumentationTests
|
||||
extends AbstractEndpointDocumentationTests {
|
||||
|
||||
private static final List<FieldDescriptor> levelFields = Arrays.asList(
|
||||
fieldWithPath("configuredLevel")
|
||||
.description("Configured level of the logger, if any.").optional(),
|
||||
fieldWithPath("effectiveLevel")
|
||||
.description("Effective level of the logger."));
|
||||
|
||||
@MockBean
|
||||
private LoggingSystem loggingSystem;
|
||||
|
||||
@Test
|
||||
public void allLoggers() throws Exception {
|
||||
given(this.loggingSystem.getSupportedLogLevels())
|
||||
.willReturn(EnumSet.allOf(LogLevel.class));
|
||||
given(this.loggingSystem.getLoggerConfigurations()).willReturn(Arrays.asList(
|
||||
new LoggerConfiguration("ROOT", LogLevel.INFO, LogLevel.INFO),
|
||||
new LoggerConfiguration("com.example", LogLevel.DEBUG, LogLevel.DEBUG)));
|
||||
this.mockMvc.perform(get("/application/loggers")).andExpect(status().isOk())
|
||||
.andDo(MockMvcRestDocumentation.document("loggers/all",
|
||||
responseFields(
|
||||
fieldWithPath("levels").description(
|
||||
"Levels support by the logging system."),
|
||||
fieldWithPath("loggers").description("Loggers keyed by name."))
|
||||
.andWithPrefix("loggers.*.", levelFields)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void logger() throws Exception {
|
||||
given(this.loggingSystem.getLoggerConfiguration("com.example")).willReturn(
|
||||
new LoggerConfiguration("com.example", LogLevel.INFO, LogLevel.INFO));
|
||||
this.mockMvc.perform(get("/application/loggers/com.example"))
|
||||
.andExpect(status().isOk()).andDo(MockMvcRestDocumentation
|
||||
.document("loggers/single", responseFields(levelFields)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setLogLevel() throws Exception {
|
||||
this.mockMvc
|
||||
.perform(post("/application/loggers/com.example")
|
||||
.content("{\"configuredLevel\":\"debug\"}")
|
||||
.contentType(MediaType.APPLICATION_JSON))
|
||||
.andExpect(status().isNoContent())
|
||||
.andDo(MockMvcRestDocumentation
|
||||
.document("loggers/set",
|
||||
requestFields(fieldWithPath("configuredLevel")
|
||||
.description("Level for the logger. May be"
|
||||
+ " omitted to clear the level.")
|
||||
.optional())));
|
||||
verify(this.loggingSystem).setLogLevel("com.example", LogLevel.DEBUG);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void clearLogLevel() throws Exception {
|
||||
this.mockMvc
|
||||
.perform(post("/application/loggers/com.example").content("{}")
|
||||
.contentType(MediaType.APPLICATION_JSON))
|
||||
.andExpect(status().isNoContent())
|
||||
.andDo(MockMvcRestDocumentation.document("loggers/clear"));
|
||||
verify(this.loggingSystem).setLogLevel("com.example", null);
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import(BaseDocumentationConfiguration.class)
|
||||
static class TestConfiguration {
|
||||
|
||||
@Bean
|
||||
public LoggersEndpoint endpoint(LoggingSystem loggingSystem) {
|
||||
return new LoggersEndpoint(loggingSystem);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.autoconfigure.endpoint.web.documentation;
|
||||
|
||||
import io.micrometer.core.instrument.Statistic;
|
||||
import io.micrometer.core.instrument.binder.jvm.JvmMemoryMetrics;
|
||||
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.boot.actuate.metrics.MetricsEndpoint;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
|
||||
import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName;
|
||||
import static org.springframework.restdocs.request.RequestDocumentation.requestParameters;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
/**
|
||||
* Tests for generating documentation describing the {@link MetricsEndpoint}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class MetricsEndpointDocumentationTests
|
||||
extends AbstractEndpointDocumentationTests {
|
||||
|
||||
@Test
|
||||
public void metricNames() throws Exception {
|
||||
this.mockMvc.perform(get("/application/metrics")).andExpect(status().isOk())
|
||||
.andDo(document("metrics/names", responseFields(fieldWithPath("names")
|
||||
.description("Names of the known metrics."))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void metric() throws Exception {
|
||||
this.mockMvc.perform(get("/application/metrics/jvm.memory.max"))
|
||||
.andExpect(status().isOk())
|
||||
.andDo(document("metrics/metric",
|
||||
responseFields(
|
||||
fieldWithPath("name").description("Name of the metric"),
|
||||
fieldWithPath("measurements")
|
||||
.description("Measurements of the metric"),
|
||||
fieldWithPath("measurements[].statistic")
|
||||
.description("Statistic of the measurement. ("
|
||||
+ describeEnumValues(Statistic.class) + ")."),
|
||||
fieldWithPath("measurements[].value")
|
||||
.description("Value of the measurement."),
|
||||
fieldWithPath("availableTags")
|
||||
.description("Tags that are available for drill-down."),
|
||||
fieldWithPath("availableTags[].tag")
|
||||
.description("Name of the tag."),
|
||||
fieldWithPath("availableTags[].values")
|
||||
.description("Possible values of the tag."))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void metricWithTags() throws Exception {
|
||||
this.mockMvc
|
||||
.perform(get("/application/metrics/jvm.memory.max")
|
||||
.param("tag", "area:nonheap").param("tag", "id:Code Cache"))
|
||||
.andExpect(status().isOk())
|
||||
.andDo(document("metrics/metric-with-tags",
|
||||
requestParameters(parameterWithName("tag").description(
|
||||
"A tag to use for drill-down in the form `name:value`."))));
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import(BaseDocumentationConfiguration.class)
|
||||
static class TestConfiguration {
|
||||
|
||||
@Bean
|
||||
public MetricsEndpoint endpoint() {
|
||||
SimpleMeterRegistry registry = new SimpleMeterRegistry();
|
||||
new JvmMemoryMetrics().bindTo(registry);
|
||||
return new MetricsEndpoint(registry);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.autoconfigure.endpoint.web.documentation;
|
||||
|
||||
import io.micrometer.core.instrument.Clock;
|
||||
import io.micrometer.core.instrument.binder.jvm.JvmMemoryMetrics;
|
||||
import io.micrometer.prometheus.PrometheusConfig;
|
||||
import io.micrometer.prometheus.PrometheusMeterRegistry;
|
||||
import io.prometheus.client.CollectorRegistry;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.boot.actuate.metrics.export.prometheus.PrometheusScrapeEndpoint;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
/**
|
||||
* Tests for generating documentation describing the {@link PrometheusScrapeEndpoint}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class PrometheusScrapeEndpointDocumentationTests
|
||||
extends AbstractEndpointDocumentationTests {
|
||||
|
||||
@Test
|
||||
public void prometheus() throws Exception {
|
||||
this.mockMvc.perform(get("/application/prometheus")).andExpect(status().isOk())
|
||||
.andDo(document("prometheus"));
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import(BaseDocumentationConfiguration.class)
|
||||
static class TestConfiguration {
|
||||
|
||||
@Bean
|
||||
public PrometheusScrapeEndpoint endpoint() {
|
||||
CollectorRegistry collectorRegistry = new CollectorRegistry(true);
|
||||
PrometheusMeterRegistry meterRegistry = new PrometheusMeterRegistry(
|
||||
new PrometheusConfig() {
|
||||
|
||||
@Override
|
||||
public String get(String key) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}, collectorRegistry, Clock.SYSTEM);
|
||||
new JvmMemoryMetrics().bindTo(meterRegistry);
|
||||
return new PrometheusScrapeEndpoint(collectorRegistry);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.autoconfigure.endpoint.web.documentation;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.boot.actuate.scheduling.ScheduledTasksEndpoint;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.restdocs.payload.FieldDescriptor;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.scheduling.config.ScheduledTaskHolder;
|
||||
|
||||
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
|
||||
import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse;
|
||||
import static org.springframework.restdocs.operation.preprocess.Preprocessors.replacePattern;
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
/**
|
||||
* Tests for generating documentation describing the {@link ScheduledTasksEndpoint}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class ScheduledTasksEndpointDocumentationTests
|
||||
extends AbstractEndpointDocumentationTests {
|
||||
|
||||
@Test
|
||||
public void scheduledTasks() throws Exception {
|
||||
this.mockMvc.perform(get("/application/scheduledtasks"))
|
||||
.andExpect(status().isOk())
|
||||
.andDo(document("scheduled-tasks",
|
||||
preprocessResponse(replacePattern(
|
||||
Pattern.compile(
|
||||
"org.*\\.ScheduledTasksEndpointDocumentationTests\\$"
|
||||
+ "TestConfiguration"),
|
||||
"com.example.Processor")),
|
||||
responseFields(
|
||||
fieldWithPath("cron").description("Cron tasks, if any."),
|
||||
targetFieldWithPrefix("cron.[]"),
|
||||
fieldWithPath("cron.[].expression")
|
||||
.description("Cron expression."),
|
||||
fieldWithPath("fixedDelay")
|
||||
.description("Fixed delay tasks, if any."),
|
||||
targetFieldWithPrefix("fixedDelay.[]"),
|
||||
initialDelayWithPrefix("fixedDelay.[]."),
|
||||
fieldWithPath("fixedDelay.[].interval").description(
|
||||
"Interval, in milliseconds, between the end of the last"
|
||||
+ " execution and the start of the next."),
|
||||
fieldWithPath("fixedRate")
|
||||
.description("Fixed rate tasks, if any."),
|
||||
targetFieldWithPrefix("fixedRate.[]."),
|
||||
fieldWithPath("fixedRate.[].interval").description(
|
||||
"Interval, in milliseconds, between the start of each execution."),
|
||||
initialDelayWithPrefix("fixedRate.[]."))));
|
||||
}
|
||||
|
||||
private FieldDescriptor targetFieldWithPrefix(String prefix) {
|
||||
return fieldWithPath(prefix + "runnable.target")
|
||||
.description("Target that will be executed.");
|
||||
}
|
||||
|
||||
private FieldDescriptor initialDelayWithPrefix(String prefix) {
|
||||
return fieldWithPath(prefix + "initialDelay")
|
||||
.description("Delay, in milliseconds, before first execution.");
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableScheduling
|
||||
@Import(BaseDocumentationConfiguration.class)
|
||||
static class TestConfiguration {
|
||||
|
||||
@Bean
|
||||
public ScheduledTasksEndpoint endpoint(Collection<ScheduledTaskHolder> holders) {
|
||||
return new ScheduledTasksEndpoint(holders);
|
||||
}
|
||||
|
||||
@Scheduled(cron = "0 0 0/3 1/1 * ?")
|
||||
public void processOrders() {
|
||||
|
||||
}
|
||||
|
||||
@Scheduled(fixedDelay = 5000, initialDelay = 5000)
|
||||
public void purge() {
|
||||
|
||||
}
|
||||
|
||||
@Scheduled(fixedRate = 3000, initialDelay = 10000)
|
||||
public void retrieveIssues() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,150 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.autoconfigure.endpoint.web.documentation;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.boot.actuate.context.ShutdownEndpoint;
|
||||
import org.springframework.boot.actuate.session.SessionsEndpoint;
|
||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.restdocs.payload.FieldDescriptor;
|
||||
import org.springframework.session.FindByIndexNameSessionRepository;
|
||||
import org.springframework.session.MapSession;
|
||||
import org.springframework.session.Session;
|
||||
import org.springframework.test.context.TestPropertySource;
|
||||
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
|
||||
import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName;
|
||||
import static org.springframework.restdocs.request.RequestDocumentation.requestParameters;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
/**
|
||||
* Tests for generating documentation describing the {@link ShutdownEndpoint}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
@TestPropertySource(properties = "spring.jackson.serialization.write-dates-as-timestamps=false")
|
||||
public class SessionsEndpointDocumentationTests
|
||||
extends AbstractEndpointDocumentationTests {
|
||||
|
||||
private static final Session sessionOne = createSession(
|
||||
Instant.now().minusSeconds(60 * 60 * 12), Instant.now().minusSeconds(45));
|
||||
|
||||
private static final Session sessionTwo = createSession(
|
||||
"4db5efcc-99cb-4d05-a52c-b49acfbb7ea9",
|
||||
Instant.now().minusSeconds(60 * 60 * 5), Instant.now().minusSeconds(37));
|
||||
|
||||
private static final Session sessionThree = createSession(
|
||||
Instant.now().minusSeconds(60 * 60 * 2), Instant.now().minusSeconds(12));
|
||||
|
||||
private static final List<FieldDescriptor> sessionFields = Arrays
|
||||
.asList(fieldWithPath("id").description("ID of the session."),
|
||||
fieldWithPath("attributeNames").description(
|
||||
"Names of the attributes stored in the session."),
|
||||
fieldWithPath("creationTime")
|
||||
.description("Timestamp of when the session was created."),
|
||||
fieldWithPath("lastAccessedTime")
|
||||
.description("Timestamp of when the session was last accessed."),
|
||||
fieldWithPath("maxInactiveInterval")
|
||||
.description("Maximum permitted period of inactivity, in seconds, "
|
||||
+ "before the session will expire."),
|
||||
fieldWithPath("expired").description("Whether the session has expired."));
|
||||
|
||||
@MockBean
|
||||
private FindByIndexNameSessionRepository<Session> sessionRepository;
|
||||
|
||||
@Test
|
||||
public void sessionsForUsername() throws Exception {
|
||||
Map<String, Session> sessions = new HashMap<>();
|
||||
sessions.put(sessionOne.getId(), sessionOne);
|
||||
sessions.put(sessionTwo.getId(), sessionTwo);
|
||||
sessions.put(sessionThree.getId(), sessionThree);
|
||||
given(this.sessionRepository.findByIndexNameAndIndexValue(
|
||||
FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, "alice"))
|
||||
.willReturn(sessions);
|
||||
this.mockMvc.perform(get("/application/sessions").param("username", "alice"))
|
||||
.andExpect(status().isOk())
|
||||
.andDo(document("sessions/username",
|
||||
responseFields(fieldWithPath("sessions")
|
||||
.description("Sessions for the given username."))
|
||||
.andWithPrefix("sessions.[].", sessionFields),
|
||||
requestParameters(parameterWithName("username")
|
||||
.description("Name of the user."))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sessionWithId() throws Exception {
|
||||
Map<String, Session> sessions = new HashMap<>();
|
||||
sessions.put(sessionOne.getId(), sessionOne);
|
||||
sessions.put(sessionTwo.getId(), sessionTwo);
|
||||
sessions.put(sessionThree.getId(), sessionThree);
|
||||
given(this.sessionRepository.findById(sessionTwo.getId())).willReturn(sessionTwo);
|
||||
this.mockMvc.perform(get("/application/sessions/{id}", sessionTwo.getId()))
|
||||
.andExpect(status().isOk())
|
||||
.andDo(document("sessions/id", responseFields(sessionFields)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deleteASession() throws Exception {
|
||||
this.mockMvc.perform(delete("/application/sessions/{id}", sessionTwo.getId()))
|
||||
.andExpect(status().isNoContent()).andDo(document("sessions/delete"));
|
||||
verify(this.sessionRepository).deleteById(sessionTwo.getId());
|
||||
}
|
||||
|
||||
private static MapSession createSession(Instant creationTime,
|
||||
Instant lastAccessedTime) {
|
||||
return createSession(UUID.randomUUID().toString(), creationTime,
|
||||
lastAccessedTime);
|
||||
}
|
||||
|
||||
private static MapSession createSession(String id, Instant creationTime,
|
||||
Instant lastAccessedTime) {
|
||||
MapSession session = new MapSession(id);
|
||||
session.setCreationTime(creationTime);
|
||||
session.setLastAccessedTime(lastAccessedTime);
|
||||
return session;
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import(BaseDocumentationConfiguration.class)
|
||||
static class TestConfiguration {
|
||||
|
||||
@Bean
|
||||
public SessionsEndpoint endpoint(
|
||||
FindByIndexNameSessionRepository<?> sessionRepository) {
|
||||
return new SessionsEndpoint(sessionRepository);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.autoconfigure.endpoint.web.documentation;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.boot.actuate.context.ShutdownEndpoint;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation;
|
||||
import org.springframework.test.context.TestPropertySource;
|
||||
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
/**
|
||||
* Tests for generating documentation describing the {@link ShutdownEndpoint}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
@TestPropertySource(properties = "endpoints.shutdown.enabled=true")
|
||||
public class ShutdownEndpointDocumentationTests
|
||||
extends AbstractEndpointDocumentationTests {
|
||||
|
||||
@Test
|
||||
public void shutdown() throws Exception {
|
||||
this.mockMvc.perform(post("/application/shutdown")).andExpect(status().isOk())
|
||||
.andDo(MockMvcRestDocumentation.document("shutdown",
|
||||
responseFields(fieldWithPath("message").description(
|
||||
"Message describing the result of the request."))));
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import(BaseDocumentationConfiguration.class)
|
||||
static class TestConfiguration {
|
||||
|
||||
@Bean
|
||||
public ShutdownEndpoint endpoint(Environment environment) {
|
||||
ShutdownEndpoint endpoint = new ShutdownEndpoint();
|
||||
endpoint.setApplicationContext(new AnnotationConfigApplicationContext());
|
||||
return endpoint;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.autoconfigure.endpoint.web.documentation;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.boot.actuate.health.CompositeHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.HealthIndicator;
|
||||
import org.springframework.boot.actuate.health.OrderedHealthAggregator;
|
||||
import org.springframework.boot.actuate.health.StatusEndpoint;
|
||||
import org.springframework.boot.actuate.jdbc.DataSourceHealthIndicator;
|
||||
import org.springframework.boot.actuate.system.DiskSpaceHealthIndicator;
|
||||
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
/**
|
||||
* Tests for generating documentation describing the {@link StatusEndpoint}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class StatusEndpointDocumentationTests extends AbstractEndpointDocumentationTests {
|
||||
|
||||
@Test
|
||||
public void health() throws Exception {
|
||||
this.mockMvc.perform(get("/application/status")).andExpect(status().isOk())
|
||||
.andDo(document("status", responseFields(fieldWithPath("status")
|
||||
.description("Overall status of the application."))));
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import(BaseDocumentationConfiguration.class)
|
||||
@ImportAutoConfiguration(DataSourceAutoConfiguration.class)
|
||||
static class TestConfiguration {
|
||||
|
||||
@Bean
|
||||
public StatusEndpoint endpoint(Map<String, HealthIndicator> healthIndicators) {
|
||||
return new StatusEndpoint(new CompositeHealthIndicator(
|
||||
new OrderedHealthAggregator(), healthIndicators));
|
||||
}
|
||||
|
||||
@Bean
|
||||
public DiskSpaceHealthIndicator diskSpaceHealthIndicator() {
|
||||
return new DiskSpaceHealthIndicator(new File("."), 1024 * 1024 * 10);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public DataSourceHealthIndicator dataSourceHealthIndicator(
|
||||
DataSource dataSource) {
|
||||
return new DataSourceHealthIndicator(dataSource);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,176 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.autoconfigure.endpoint.web.documentation;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.boot.actuate.management.ThreadDumpEndpoint;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation;
|
||||
import org.springframework.restdocs.payload.JsonFieldType;
|
||||
|
||||
import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse;
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.subsectionWithPath;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
/**
|
||||
* Tests for generating documentation describing {@link ThreadDumpEndpoint}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class ThreadDumpEndpointDocumentationTests
|
||||
extends AbstractEndpointDocumentationTests {
|
||||
|
||||
@Test
|
||||
public void threadDump() throws Exception {
|
||||
this.mockMvc.perform(get("/application/threaddump")).andExpect(status().isOk())
|
||||
.andDo(MockMvcRestDocumentation.document("threaddump",
|
||||
preprocessResponse(limit("threads")),
|
||||
responseFields(
|
||||
fieldWithPath("threads").description("JVM's threads."),
|
||||
fieldWithPath("threads.[].threadName")
|
||||
.description("Name of the thread."),
|
||||
fieldWithPath("threads.[].threadId")
|
||||
.description("ID of the thread."),
|
||||
fieldWithPath("threads.[].blockedTime").description(
|
||||
"Time in milliseconds that the thread has spent "
|
||||
+ "blocked. -1 if thread contention "
|
||||
+ "monitoring is disabled."),
|
||||
fieldWithPath("threads.[].blockedCount").description(
|
||||
"Total number of times that the thread has been "
|
||||
+ "blocked."),
|
||||
fieldWithPath("threads.[].waitedTime").description(
|
||||
"Time in milliseconds that the thread has spent "
|
||||
+ "waiting. -1 if thread contention "
|
||||
+ "monitoring is disabled"),
|
||||
fieldWithPath("threads.[].waitedCount").description(
|
||||
"Total number of times that the thread has waited"
|
||||
+ " for notification."),
|
||||
fieldWithPath("threads.[].lockName")
|
||||
.description(
|
||||
"Description of the object on which the "
|
||||
+ "thread is blocked, if any.")
|
||||
.optional(),
|
||||
fieldWithPath("threads.[].lockOwnerId").description(
|
||||
"ID of the thread that owns the object on which "
|
||||
+ "the thread is blocked. `-1` if the "
|
||||
+ "thread is not blocked."),
|
||||
fieldWithPath("threads.[].lockOwnerName")
|
||||
.description(
|
||||
"Name of the thread that owns the object "
|
||||
+ "on which the thread is blocked.")
|
||||
.optional(),
|
||||
fieldWithPath("threads.[].inNative").description(
|
||||
"Whether the thread is executing native code."),
|
||||
fieldWithPath("threads.[].suspended")
|
||||
.description("Whether the thread is suspended."),
|
||||
fieldWithPath("threads.[].threadState")
|
||||
.description("State of the thread ("
|
||||
+ describeEnumValues(Thread.State.class)
|
||||
+ ")."),
|
||||
fieldWithPath("threads.[].stackTrace")
|
||||
.description("Stack trace of the thread."),
|
||||
fieldWithPath("threads.[].stackTrace.[].methodName")
|
||||
.description("Name of the method."),
|
||||
fieldWithPath("threads.[].stackTrace.[].fileName")
|
||||
.description(
|
||||
"Name of the source file that contains "
|
||||
+ "the execution point identified "
|
||||
+ "by this entry, if any.")
|
||||
.optional().type(JsonFieldType.STRING),
|
||||
fieldWithPath("threads.[].stackTrace.[].lineNumber")
|
||||
.description("Line number of the execution point "
|
||||
+ "identified by this entry. Negative"
|
||||
+ " if unknown."),
|
||||
fieldWithPath("threads.[].stackTrace.[].className")
|
||||
.description(
|
||||
"Name of the class that contains the "
|
||||
+ "execution point identified "
|
||||
+ "by this entry."),
|
||||
fieldWithPath("threads.[].stackTrace.[].nativeMethod")
|
||||
.description(
|
||||
"Whether the execution point is a native "
|
||||
+ "method."),
|
||||
fieldWithPath("threads.[].lockedMonitors").description(
|
||||
"Monitors locked by this thread, if any"),
|
||||
fieldWithPath("threads.[].lockedMonitors.[].className")
|
||||
.description("Class name of the lock object.")
|
||||
.optional().type(JsonFieldType.STRING),
|
||||
fieldWithPath(
|
||||
"threads.[].lockedMonitors.[].identityHashCode")
|
||||
.description(
|
||||
"Identity hash code of the lock "
|
||||
+ "object.")
|
||||
.optional().type(JsonFieldType.NUMBER),
|
||||
fieldWithPath(
|
||||
"threads.[].lockedMonitors.[].lockedStackDepth")
|
||||
.description(
|
||||
"Stack depth where the monitor "
|
||||
+ "was locked.")
|
||||
.optional().type(JsonFieldType.NUMBER),
|
||||
subsectionWithPath(
|
||||
"threads.[].lockedMonitors.[].lockedStackFrame")
|
||||
.description(
|
||||
"Stack frame that locked the "
|
||||
+ "monitor.")
|
||||
.optional().type(JsonFieldType.OBJECT),
|
||||
fieldWithPath("threads.[].lockedSynchronizers")
|
||||
.description(
|
||||
"Synchronizers locked by this thread."),
|
||||
fieldWithPath(
|
||||
"threads.[].lockedSynchronizers.[].className")
|
||||
.description("Class name of the locked "
|
||||
+ "synchronizer.")
|
||||
.optional().type(JsonFieldType.STRING),
|
||||
fieldWithPath(
|
||||
"threads.[].lockedSynchronizers.[].identifyHashCode")
|
||||
.description(
|
||||
"Identity hash code of the locked "
|
||||
+ "synchronizer.")
|
||||
.optional().type(JsonFieldType.NUMBER),
|
||||
fieldWithPath("threads.[].lockInfo").description(
|
||||
"Object for which the thread is blocked "
|
||||
+ "waiting.")
|
||||
.optional(),
|
||||
fieldWithPath("threads.[].lockInfo.className")
|
||||
.description(
|
||||
"Fully qualified class name of the lock"
|
||||
+ " object.")
|
||||
.optional(),
|
||||
fieldWithPath("threads.[].lockInfo.identityHashCode")
|
||||
.description(
|
||||
"Identity hash code of the lock object.")
|
||||
.optional())));
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import(BaseDocumentationConfiguration.class)
|
||||
static class TestConfiguration {
|
||||
|
||||
@Bean
|
||||
public ThreadDumpEndpoint endpoint() {
|
||||
return new ThreadDumpEndpoint();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1 @@
|
||||
com.example.cache.max-size: 1000
|
@ -0,0 +1,31 @@
|
||||
. ____ _ __ _ _
|
||||
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
|
||||
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
|
||||
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
|
||||
' |____| .__|_| |_|_| |_\__, | / / / /
|
||||
=========|_|==============|___/=/_/_/_/
|
||||
:: Spring Boot ::
|
||||
|
||||
2017-08-08 17:12:30.910 INFO 19866 --- [ main] s.f.SampleWebFreeMarkerApplication : Starting SampleWebFreeMarkerApplication on host.local with PID 19866
|
||||
2017-08-08 17:12:30.913 INFO 19866 --- [ main] s.f.SampleWebFreeMarkerApplication : No active profile set, falling back to default profiles: default
|
||||
2017-08-08 17:12:30.952 INFO 19866 --- [ main] ConfigServletWebServerApplicationContext : Refreshing org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@76b10754: startup date [Tue Aug 08 17:12:30 BST 2017]; root of context hierarchy
|
||||
2017-08-08 17:12:31.878 INFO 19866 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
|
||||
2017-08-08 17:12:31.889 INFO 19866 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
|
||||
2017-08-08 17:12:31.890 INFO 19866 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet Engine: Apache Tomcat/8.5.16
|
||||
2017-08-08 17:12:31.978 INFO 19866 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
|
||||
2017-08-08 17:12:31.978 INFO 19866 --- [ost-startStop-1] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 1028 ms
|
||||
2017-08-08 17:12:32.080 INFO 19866 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean : Mapping servlet: 'dispatcherServlet' to [/]
|
||||
2017-08-08 17:12:32.084 INFO 19866 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'characterEncodingFilter' to: [/*]
|
||||
2017-08-08 17:12:32.084 INFO 19866 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
|
||||
2017-08-08 17:12:32.084 INFO 19866 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'httpPutFormContentFilter' to: [/*]
|
||||
2017-08-08 17:12:32.084 INFO 19866 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'requestContextFilter' to: [/*]
|
||||
2017-08-08 17:12:32.349 INFO 19866 --- [ main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@76b10754: startup date [Tue Aug 08 17:12:30 BST 2017]; root of context hierarchy
|
||||
2017-08-08 17:12:32.420 INFO 19866 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error]}" onto public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
|
||||
2017-08-08 17:12:32.421 INFO 19866 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],produces=[text/html]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
|
||||
2017-08-08 17:12:32.444 INFO 19866 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
|
||||
2017-08-08 17:12:32.444 INFO 19866 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
|
||||
2017-08-08 17:12:32.471 INFO 19866 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
|
||||
2017-08-08 17:12:32.600 INFO 19866 --- [ main] o.s.w.s.v.f.FreeMarkerConfigurer : ClassTemplateLoader for Spring macros added to FreeMarker configuration
|
||||
2017-08-08 17:12:32.681 INFO 19866 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup
|
||||
2017-08-08 17:12:32.744 INFO 19866 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http)
|
||||
2017-08-08 17:12:32.750 INFO 19866 --- [ main] s.f.SampleWebFreeMarkerApplication : Started SampleWebFreeMarkerApplication in 2.172 seconds (JVM running for 2.479)
|
Loading…
Reference in New Issue