Skip to content

Commit ef986b1

Browse files
committed
Polish Quartz endpoint documentation
See gh-10364
1 parent a07c8e6 commit ef986b1

File tree

3 files changed

+96
-55
lines changed

3 files changed

+96
-55
lines changed

spring-boot-project/spring-boot-actuator-autoconfigure/src/docs/asciidoc/endpoints/quartz.adoc

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,12 @@ include::{snippets}/quartz/trigger-details-cron/curl-request.adoc[]
157157

158158
The preceding example retrieves the details of trigger identified by the `samples` group and `example` name.
159159

160-
The resulting response has a common structure and a specific additional object according to the trigger implementation.
160+
161+
162+
[[quartz-trigger-common-response-structure]]
163+
=== Common Response Structure
164+
165+
The response has a common structure and an additional object that is specific to the trigger's type.
161166
There are five supported types:
162167

163168
* `cron` for `CronTrigger`
@@ -166,9 +171,14 @@ There are five supported types:
166171
* `calendarInterval` for `CalendarIntervalTrigger`
167172
* `custom` for any other trigger implementations
168173

174+
The following table describes the structure of the common elements of the response:
175+
176+
[cols="2,1,3"]
177+
include::{snippets}/quartz/trigger-details-common/response-fields.adoc[]
178+
169179

170180

171-
[[quartz-trigger-cron]]
181+
[[quartz-trigger-cron-response-structure]]
172182
=== Cron Trigger Response Structure
173183

174184
A cron trigger defines the cron expression that is used to determine when it has to fire.
@@ -177,14 +187,16 @@ The resulting response for such a trigger implementation is similar to the follo
177187
include::{snippets}/quartz/trigger-details-cron/http-response.adoc[]
178188

179189

180-
The following table describes the structure of the response:
190+
Much of the response is common to all trigger types.
191+
The structure of the common elements of the response was <<quartz-trigger-common-response-structure,described previously>>.
192+
The following table describes the structure of the parts of the response that are specific to cron triggers:
181193

182194
[cols="2,1,3"]
183195
include::{snippets}/quartz/trigger-details-cron/response-fields.adoc[]
184196

185197

186198

187-
[[quartz-trigger-simple]]
199+
[[quartz-trigger-simple-response-structure]]
188200
=== Simple Trigger Response Structure
189201

190202
A simple trigger is used to fire a Job at a given moment in time, and optionally repeated at a specified interval.
@@ -193,14 +205,16 @@ The resulting response for such a trigger implementation is similar to the follo
193205
include::{snippets}/quartz/trigger-details-simple/http-response.adoc[]
194206

195207

196-
The following table describes the structure of the response:
208+
Much of the response is common to all trigger types.
209+
The structure of the common elements of the response was <<quartz-trigger-common-response-structure,described previously>>.
210+
The following table describes the structure of the parts of the response that are specific to simple triggers:
197211

198212
[cols="2,1,3"]
199213
include::{snippets}/quartz/trigger-details-simple/response-fields.adoc[]
200214

201215

202216

203-
[[quartz-trigger-daily-time-interval]]
217+
[[quartz-trigger-daily-time-interval-response-structure]]
204218
=== Daily Time Interval Trigger Response Structure
205219

206220
A daily time interval trigger is used to fire a Job based upon daily repeating time intervals.
@@ -209,30 +223,34 @@ The resulting response for such a trigger implementation is similar to the follo
209223
include::{snippets}/quartz/trigger-details-daily-time-interval/http-response.adoc[]
210224

211225

212-
The following table describes the structure of the response:
226+
Much of the response is common to all trigger types.
227+
The structure of the common elements of the response was <<quartz-trigger-common-response-structure,described previously>>.
228+
The following table describes the structure of the parts of the response that are specific to daily time interval triggers:
213229

214230
[cols="2,1,3"]
215231
include::{snippets}/quartz/trigger-details-daily-time-interval/response-fields.adoc[]
216232

217233

218234

219-
[[quartz-trigger-calendar-interval]]
235+
[[quartz-trigger-calendar-interval-response-structure]]
220236
=== Calendar Interval Trigger Response Structure
221237

222-
A daily time interval trigger is used to fire a Job based upon repeating calendar time intervals.
238+
A calendar interval trigger is used to fire a Job based upon repeating calendar time intervals.
223239
The resulting response for such a trigger implementation is similar to the following:
224240

225241
include::{snippets}/quartz/trigger-details-calendar-interval/http-response.adoc[]
226242

227243

228-
The following table describes the structure of the response:
244+
Much of the response is common to all trigger types.
245+
The structure of the common elements of the response was <<quartz-trigger-common-response-structure,described previously>>.
246+
The following table describes the structure of the parts of the response that are specific to calendar interval triggers:
229247

230248
[cols="2,1,3"]
231249
include::{snippets}/quartz/trigger-details-calendar-interval/response-fields.adoc[]
232250

233251

234252

235-
[[quartz-trigger-custom]]
253+
[[quartz-trigger-custom-response-structure]]
236254
=== Custom Trigger Response Structure
237255

238256
A custom trigger is any other implementation.
@@ -241,7 +259,9 @@ The resulting response for such a trigger implementation is similar to the follo
241259
include::{snippets}/quartz/trigger-details-custom/http-response.adoc[]
242260

243261

244-
The following table describes the structure of the response:
262+
Much of the response is common to all trigger types.
263+
The structure of the common elements of the response was <<quartz-trigger-common-response-structure,described previously>>.
264+
The following table describes the structure of the parts of the response that are specific to custom triggers:
245265

246266
[cols="2,1,3"]
247267
include::{snippets}/quartz/trigger-details-custom/response-fields.adoc[]

spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/AbstractEndpointDocumentationTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@
5959
"management.endpoints.web.exposure.include=*", "spring.jackson.default-property-inclusion=non_null" })
6060
public abstract class AbstractEndpointDocumentationTests {
6161

62-
protected String describeEnumValues(Class<? extends Enum<?>> enumType) {
62+
protected static String describeEnumValues(Class<? extends Enum<?>> enumType) {
6363
return StringUtils.collectionToDelimitedString(Stream.of(enumType.getEnumConstants())
6464
.map((constant) -> "`" + constant.name() + "`").collect(Collectors.toList()), ", ");
6565
}

spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/QuartzEndpointDocumentationTests.java

Lines changed: 63 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,9 @@
6767
import static org.mockito.Mockito.mock;
6868
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
6969
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
70+
import static org.springframework.restdocs.payload.PayloadDocumentation.relaxedResponseFields;
7071
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
72+
import static org.springframework.restdocs.payload.PayloadDocumentation.subsectionWithPath;
7173
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
7274
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
7375

@@ -88,24 +90,24 @@ class QuartzEndpointDocumentationTests extends MockMvcEndpointDocumentationTests
8890

8991
private static final JobDetail jobThree = JobBuilder.newJob(Job.class).withIdentity("jobThree", "tests").build();
9092

91-
private static final CronTrigger triggerOne = TriggerBuilder.newTrigger().forJob(jobOne).withPriority(3)
93+
private static final CronTrigger cronTrigger = TriggerBuilder.newTrigger().forJob(jobOne).withPriority(3)
9294
.withDescription("3AM on weekdays").withIdentity("3am-weekdays", "samples")
9395
.withSchedule(
9496
CronScheduleBuilder.atHourAndMinuteOnGivenDaysOfWeek(3, 0, 1, 2, 3, 4, 5).inTimeZone(timeZone))
9597
.build();
9698

97-
private static final SimpleTrigger triggerTwo = TriggerBuilder.newTrigger().forJob(jobOne).withPriority(7)
99+
private static final SimpleTrigger simpleTrigger = TriggerBuilder.newTrigger().forJob(jobOne).withPriority(7)
98100
.withDescription("Once a day").withIdentity("every-day", "samples")
99101
.withSchedule(SimpleScheduleBuilder.repeatHourlyForever(24)).build();
100102

101-
private static final CalendarIntervalTrigger triggerThree = TriggerBuilder.newTrigger().forJob(jobTwo)
103+
private static final CalendarIntervalTrigger calendarIntervalTrigger = TriggerBuilder.newTrigger().forJob(jobTwo)
102104
.withDescription("Once a week").withIdentity("once-a-week", "samples")
103105
.withSchedule(CalendarIntervalScheduleBuilder.calendarIntervalSchedule().withIntervalInWeeks(1)
104106
.inTimeZone(timeZone))
105107
.build();
106108

107-
private static final DailyTimeIntervalTrigger triggerFour = TriggerBuilder.newTrigger().forJob(jobThree)
108-
.withDescription("Every hour between 9AM and 6PM on Tuesday and Thursday")
109+
private static final DailyTimeIntervalTrigger dailyTimeIntervalTrigger = TriggerBuilder.newTrigger()
110+
.forJob(jobThree).withDescription("Every hour between 9AM and 6PM on Tuesday and Thursday")
109111
.withIdentity("every-hour-tue-thu")
110112
.withSchedule(DailyTimeIntervalScheduleBuilder.dailyTimeIntervalSchedule()
111113
.onDaysOfTheWeek(Calendar.TUESDAY, Calendar.THURSDAY)
@@ -148,9 +150,10 @@ class QuartzEndpointDocumentationTests extends MockMvcEndpointDocumentationTests
148150
fieldWithPath("name").description("Name of the trigger."),
149151
fieldWithPath("description").description("Description of the trigger, if any."),
150152
fieldWithPath("state")
151-
.description("State of the trigger, can be NONE, NORMAL, PAUSED, COMPLETE, ERROR, or BLOCKED."),
153+
.description("State of the trigger (" + describeEnumValues(TriggerState.class) + ")."),
152154
fieldWithPath("type").description(
153-
"Type of the trigger, determine the key of the object containing implementation-specific details."),
155+
"Type of the trigger (`calendarInterval`, `cron`, `custom`, `dailyTimeInterval`, `simple`). "
156+
+ "Determines the key of the object containing type-specific details."),
154157
fieldWithPath("calendarName").description("Name of the Calendar associated with this Trigger, if any."),
155158
startTime(""), endTime(""), previousFireTime(""), nextFireTime(""), priority(""),
156159
fieldWithPath("finalFireTime").optional().type(JsonFieldType.STRING)
@@ -164,7 +167,7 @@ class QuartzEndpointDocumentationTests extends MockMvcEndpointDocumentationTests
164167
@Test
165168
void quartzReport() throws Exception {
166169
mockJobs(jobOne, jobTwo, jobThree);
167-
mockTriggers(triggerOne, triggerTwo, triggerThree, triggerFour);
170+
mockTriggers(cronTrigger, simpleTrigger, calendarIntervalTrigger, dailyTimeIntervalTrigger);
168171
this.mockMvc.perform(get("/actuator/quartz")).andExpect(status().isOk())
169172
.andDo(document("quartz/report",
170173
responseFields(fieldWithPath("jobs.groups").description("An array of job group names."),
@@ -181,7 +184,7 @@ void quartzJobs() throws Exception {
181184

182185
@Test
183186
void quartzTriggers() throws Exception {
184-
mockTriggers(triggerOne, triggerTwo, triggerThree, triggerFour);
187+
mockTriggers(cronTrigger, simpleTrigger, calendarIntervalTrigger, dailyTimeIntervalTrigger);
185188
this.mockMvc.perform(get("/actuator/quartz/triggers")).andExpect(status().isOk())
186189
.andDo(document("quartz/triggers",
187190
responseFields(fieldWithPath("groups").description("Trigger groups keyed by name."),
@@ -202,16 +205,17 @@ void quartzJobGroup() throws Exception {
202205

203206
@Test
204207
void quartzTriggerGroup() throws Exception {
205-
CronTrigger cron = triggerOne.getTriggerBuilder().startAt(fromUtc("2020-11-30T17:00:00Z"))
208+
CronTrigger cron = cronTrigger.getTriggerBuilder().startAt(fromUtc("2020-11-30T17:00:00Z"))
206209
.endAt(fromUtc("2020-12-30T03:00:00Z")).withIdentity("3am-week", "tests").build();
207210
setPreviousNextFireTime(cron, "2020-12-04T03:00:00Z", "2020-12-07T03:00:00Z");
208-
SimpleTrigger simple = triggerTwo.getTriggerBuilder().withIdentity("every-day", "tests").build();
211+
SimpleTrigger simple = simpleTrigger.getTriggerBuilder().withIdentity("every-day", "tests").build();
209212
setPreviousNextFireTime(simple, null, "2020-12-04T12:00:00Z");
210-
CalendarIntervalTrigger calendarInterval = triggerThree.getTriggerBuilder().withIdentity("once-a-week", "tests")
211-
.startAt(fromUtc("2019-07-10T14:00:00Z")).endAt(fromUtc("2023-01-01T12:00:00Z")).build();
213+
CalendarIntervalTrigger calendarInterval = calendarIntervalTrigger.getTriggerBuilder()
214+
.withIdentity("once-a-week", "tests").startAt(fromUtc("2019-07-10T14:00:00Z"))
215+
.endAt(fromUtc("2023-01-01T12:00:00Z")).build();
212216
setPreviousNextFireTime(calendarInterval, "2020-12-02T14:00:00Z", "2020-12-08T14:00:00Z");
213-
DailyTimeIntervalTrigger tueThuTrigger = triggerFour.getTriggerBuilder().withIdentity("tue-thu", "tests")
214-
.build();
217+
DailyTimeIntervalTrigger tueThuTrigger = dailyTimeIntervalTrigger.getTriggerBuilder()
218+
.withIdentity("tue-thu", "tests").build();
215219
Trigger customTrigger = mock(Trigger.class);
216220
given(customTrigger.getKey()).willReturn(TriggerKey.triggerKey("once-a-year-custom", "tests"));
217221
given(customTrigger.toString()).willReturn("com.example.CustomTrigger@fdsfsd");
@@ -242,9 +246,9 @@ void quartzTriggerGroup() throws Exception {
242246
@Test
243247
void quartzJob() throws Exception {
244248
mockJobs(jobOne);
245-
CronTrigger firstTrigger = triggerOne.getTriggerBuilder().build();
249+
CronTrigger firstTrigger = cronTrigger.getTriggerBuilder().build();
246250
setPreviousNextFireTime(firstTrigger, null, "2020-12-07T03:00:00Z");
247-
SimpleTrigger secondTrigger = triggerTwo.getTriggerBuilder().build();
251+
SimpleTrigger secondTrigger = simpleTrigger.getTriggerBuilder().build();
248252
setPreviousNextFireTime(secondTrigger, "2020-12-04T03:00:00Z", "2020-12-04T12:00:00Z");
249253
mockTriggers(firstTrigger, secondTrigger);
250254
given(this.scheduler.getTriggersOfJob(jobOne.getKey()))
@@ -266,53 +270,71 @@ void quartzJob() throws Exception {
266270
previousFireTime("triggers.[]."), nextFireTime("triggers.[]."), priority("triggers.[]."))));
267271
}
268272

273+
@Test
274+
void quartzTriggerCommon() throws Exception {
275+
setupTriggerDetails(cronTrigger.getTriggerBuilder(), TriggerState.NORMAL);
276+
this.mockMvc.perform(get("/actuator/quartz/triggers/samples/example")).andExpect(status().isOk())
277+
.andDo(document("quartz/trigger-details-common", responseFields(commonCronDetails).and(
278+
subsectionWithPath("calendarInterval").description(
279+
"Calendar time interval trigger details, if any. Present when `type` is `calendarInterval`.")
280+
.optional().type(JsonFieldType.OBJECT),
281+
subsectionWithPath("custom")
282+
.description("Custom trigger details, if any. Present when `type` is `custom`.")
283+
.optional().type(JsonFieldType.OBJECT),
284+
subsectionWithPath("cron")
285+
.description("Cron trigger details, if any. Present when `type` is `cron`.").optional()
286+
.type(JsonFieldType.OBJECT),
287+
subsectionWithPath("dailyTimeInterval").description(
288+
"Daily time interval trigger details, if any. Present when `type` is `dailyTimeInterval`.")
289+
.optional().type(JsonFieldType.OBJECT),
290+
subsectionWithPath("simple")
291+
.description("Simple trigger details, if any. Present when `type` is `simple`.")
292+
.optional().type(JsonFieldType.OBJECT))));
293+
}
294+
269295
@Test
270296
void quartzTriggerCron() throws Exception {
271-
setupTriggerDetails(triggerOne.getTriggerBuilder(), TriggerState.NORMAL);
297+
setupTriggerDetails(cronTrigger.getTriggerBuilder(), TriggerState.NORMAL);
272298
this.mockMvc.perform(get("/actuator/quartz/triggers/samples/example")).andExpect(status().isOk())
273299
.andDo(document("quartz/trigger-details-cron",
274-
responseFields(commonCronDetails)
275-
.and(fieldWithPath("cron").description("Cron trigger specific details."))
300+
relaxedResponseFields(fieldWithPath("cron").description("Cron trigger specific details."))
276301
.andWithPrefix("cron.", cronTriggerSummary)));
277302
}
278303

279304
@Test
280305
void quartzTriggerSimple() throws Exception {
281-
setupTriggerDetails(triggerTwo.getTriggerBuilder(), TriggerState.NORMAL);
306+
setupTriggerDetails(simpleTrigger.getTriggerBuilder(), TriggerState.NORMAL);
282307
this.mockMvc.perform(get("/actuator/quartz/triggers/samples/example")).andExpect(status().isOk())
283308
.andDo(document("quartz/trigger-details-simple",
284-
responseFields(commonCronDetails)
285-
.and(fieldWithPath("simple").description("Simple trigger specific details."))
309+
relaxedResponseFields(fieldWithPath("simple").description("Simple trigger specific details."))
286310
.andWithPrefix("simple.", simpleTriggerSummary)
287311
.and(repeatCount("simple."), timesTriggered("simple."))));
288312
}
289313

290314
@Test
291315
void quartzTriggerCalendarInterval() throws Exception {
292-
setupTriggerDetails(triggerThree.getTriggerBuilder(), TriggerState.NORMAL);
316+
setupTriggerDetails(calendarIntervalTrigger.getTriggerBuilder(), TriggerState.NORMAL);
293317
this.mockMvc.perform(get("/actuator/quartz/triggers/samples/example")).andExpect(status().isOk())
294-
.andDo(document("quartz/trigger-details-calendar-interval", responseFields(commonCronDetails)
295-
.and(fieldWithPath("calendarInterval")
296-
.description("Calendar interval trigger specific details."))
297-
.andWithPrefix("calendarInterval.", calendarIntervalTriggerSummary)
298-
.and(timesTriggered("calendarInterval."),
299-
fieldWithPath("calendarInterval.preserveHourOfDayAcrossDaylightSavings").description(
300-
"Whether to fire the trigger at the same time of day, regardless of daylight "
301-
+ "saving time transitions."),
302-
fieldWithPath("calendarInterval.skipDayIfHourDoesNotExist").description(
303-
"Whether to skip if the hour of the day does not exist on a given day."))));
318+
.andDo(document("quartz/trigger-details-calendar-interval", relaxedResponseFields(
319+
fieldWithPath("calendarInterval").description("Calendar interval trigger specific details."))
320+
.andWithPrefix("calendarInterval.", calendarIntervalTriggerSummary)
321+
.and(timesTriggered("calendarInterval."), fieldWithPath(
322+
"calendarInterval.preserveHourOfDayAcrossDaylightSavings").description(
323+
"Whether to fire the trigger at the same time of day, regardless of daylight "
324+
+ "saving time transitions."),
325+
fieldWithPath("calendarInterval.skipDayIfHourDoesNotExist").description(
326+
"Whether to skip if the hour of the day does not exist on a given day."))));
304327
}
305328

306329
@Test
307330
void quartzTriggerDailyTimeInterval() throws Exception {
308-
setupTriggerDetails(triggerFour.getTriggerBuilder(), TriggerState.PAUSED);
331+
setupTriggerDetails(dailyTimeIntervalTrigger.getTriggerBuilder(), TriggerState.PAUSED);
309332
this.mockMvc.perform(get("/actuator/quartz/triggers/samples/example")).andExpect(status().isOk())
310333
.andDo(document("quartz/trigger-details-daily-time-interval",
311-
responseFields(commonCronDetails)
312-
.and(fieldWithPath("dailyTimeInterval")
313-
.description("Daily time interval trigger specific details."))
314-
.andWithPrefix("dailyTimeInterval.", dailyTimeIntervalTriggerSummary)
315-
.and(repeatCount("dailyTimeInterval."), timesTriggered("dailyTimeInterval."))));
334+
relaxedResponseFields(fieldWithPath("dailyTimeInterval")
335+
.description("Daily time interval trigger specific details."))
336+
.andWithPrefix("dailyTimeInterval.", dailyTimeIntervalTriggerSummary)
337+
.and(repeatCount("dailyTimeInterval."), timesTriggered("dailyTimeInterval."))));
316338
}
317339

318340
@Test
@@ -331,8 +353,7 @@ void quartzTriggerCustom() throws Exception {
331353
mockTriggers(trigger);
332354
this.mockMvc.perform(get("/actuator/quartz/triggers/samples/example")).andExpect(status().isOk())
333355
.andDo(document("quartz/trigger-details-custom",
334-
responseFields(commonCronDetails)
335-
.and(fieldWithPath("custom").description("Custom trigger specific details."))
356+
relaxedResponseFields(fieldWithPath("custom").description("Custom trigger specific details."))
336357
.andWithPrefix("custom.", customTriggerSummary)));
337358
}
338359

0 commit comments

Comments
 (0)