-
-
Notifications
You must be signed in to change notification settings - Fork 9.4k
Implement snooze functionality for administrative monitors with… #26244
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Implement snooze functionality for administrative monitors with… #26244
Conversation
… cleanup and validation
mawinter69
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we really need minute precision for this? It will be hard to know how many minutes you have to enter when you want to snooze for a couple of days or even weeks. Hours or even days might be sufficient. Maybe instead use an input with a datetime picker.
Having the input field next to the button doesn't look that good. Already in your screenshot the placeholder is cut off. Might be worth to check if using a dialog where you choose the snooze time is better. There you could easily provide a drop down with some predefined values and a custom value.
You could just do some javascript that does a post to the doSnooze method and on success just hides the message. That would avoid a page reload.
|
|
||
| UserPreferencesProperty.DisplayName=Preferences | ||
|
|
||
| snooze=Snooze |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is not accessible from jelly files. You can see in your screenshot that the values are not used.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are right the previous package mismatch caused the translation lookup to fail. I have now moved the form into a dedicated t:snooze taglib at lib/hudson/snooze.jelly with its own snooze.properties file in the same folder
| <f:submit name="yes" value="${%Manage}"/> | ||
| <f:submit name="no" value="${%Dismiss}"/> | ||
| </form> | ||
| <form method="post" action="${rootURL}/${it.url}/snooze" style="display: inline-flex; gap: 0.5rem; align-items: center;"> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks identical in all 3 places. So that might be better placed in a taglib for easy reuse.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Refactored the duplicate code into a reusable t:snooze tag in lib/hudson.
| <f:submit name="yes" value="${%Manage}"/> | ||
| <f:submit name="no" value="${%Dismiss}"/> | ||
| </form> | ||
| <form method="post" action="${rootURL}/${it.url}/snooze" style="display: inline-flex; gap: 0.5rem; align-items: center;"> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would prefer that this is defined in a css file
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have replaced the inline styles with core utility classes.
| String duration = req.getParameter("duration"); | ||
| long durationMs; | ||
| try { | ||
| if ("custom".equals(duration)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't that always true as the 'duration' field has a default of custom and there is no code that will ever change this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I included that for future flexibility, but you are right it is redundant since the current UI only sends custom. I will simplify the logic to handle the custom path directly and remove the unnecessary check.
| <f:submit name="no" value="${%Dismiss}"/> | ||
| </form> | ||
| <form method="post" action="${rootURL}/${it.url}/snooze" style="display: inline-flex; gap: 0.5rem; align-items: center;"> | ||
| <j:if test="${h.isCrumbEncoded()}"> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this needed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I manually added the CSRF crumb logic to ensure the snooze action was secure, but I have since realized this is redundant as Jenkins global JavaScript automatically injects crumbs into all POST forms. I will remove these lines along with the manual flex styling to keep the implementation clean and consistent with the existing patterns in the codebase.
mawinter69
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You might want to take a look at #23859 where I implemented token expiration. This also has a dialog with a select for preset expirations and a custom input field for a date select.
| } | ||
|
|
||
| /** | ||
| * @since 2.549 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please don't set a version yet but write TODO. You don't know how fast your change will be merged. The version will be filled in later automatically.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please use the built-in support for dialogs. See https://weekly.ci.jenkins.io/design-library/dialogs/ how to use it
| <label for="snooze-duration-${it.id}" class="jenkins-form-label" style="display:block; margin-bottom: 5px;"> | ||
| ${%snooze.duration.label} | ||
| </label> | ||
| <select id="snooze-duration-${it.id}" name="durationPreset" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The select will not be styled like other selects in Jenkins
| </button> | ||
|
|
||
| <dialog id="snooze-dialog-${it.id}" class="snooze-dialog"> | ||
| <form id="snooze-form-${it.id}" method="post" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use <f:form
| </select> | ||
| </div> | ||
|
|
||
| <div class="jenkins-form-item"> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That could be just <f:number ...
| private final Map<String, Long> snoozedAdministrativeMonitors = new HashMap<>(); | ||
|
|
||
| /** | ||
| * @since 2.549 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
-> TODO
| } | ||
|
|
||
| /** | ||
| * @since 2.549 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
-> TODO
| } | ||
|
|
||
| /** | ||
| * @since 2.549 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
-> TODO
| } | ||
|
|
||
| /** | ||
| * @since 2.549 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
-> TODO
| <input type="number" id="snooze-minutes-${it.id}" name="customMinutes" | ||
| placeholder="${%snooze.duration.placeholder}" min="1" max="525600" | ||
| class="jenkins-input snooze-custom-input" | ||
| style="display: none; width: 100%;" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In general when you want to hide something, set the class jenkins-hidden, to later show it just remove it from the element. That avoids that you need to know which exact value is set for display when it is shown.
mawinter69
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| Uses AJAX to avoid page reload. | ||
| </st:documentation> | ||
|
|
||
| <st:adjunct includes="lib.hudson.snoozeForm"/> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You need to rename the .js and the .css files. What happens now is that the adjunct will also include the snoozeForm.jelly, leading to a duplication of things
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By using the built-in dialog support you will most likely not need any css. It will work out of the box also with dark theme or other themes


Fixes #16641
Testing done
Automated Testing:
Created core/src/test/java/hudson/model/AdministrativeMonitorTest.java with 8 new unit tests covering:
Snooze expiry logic (testSnoozeExpiry).
Cleanup of expired monitors (testCleanupRemovesOnlyThisMonitor).
Persistence of snoozed state across restarts (testSnoozePersistence).
Isolation between multiple monitors (testMultipleMonitorsIndependent).
Validation of duration input (negative, zero, excessive duration checks).
Ran mvn test -Dtest=AdministrativeMonitorTest (Passed: 8/8 tests).
Ran mvn clean verify to ensure no regressions in established tests.
Manual Verification:
Verified Internationalization:
Checked
core/src/main/resources/hudson/model/Messages.propertiesfor presence ofSnooze,Snooze.duration.label, etc.Verified Accessibility:
Inspected Jelly files (
BuiltInNodeMigration,ControllerExecutorsAgents,ControllerExecutorsNoAgents) to ensure<label>,id,aria-label, andtitleattributes were correctly added to input fields.Verified Javadoc:
Confirmed
AdministrativeMonitor.javaJavadoc formatting matches master branch (no diff noise).Screenshots (UI changes only)
Before
After
Proposed changelog entries
Allow administrative monitors to be snoozed for a specific duration (up to 1 year).
Proposed changelog category
/label rfe
Proposed upgrade guidelines
N/A
Submitter checklist
@Restrictedor have@since TODOJavadocs, as appropriate.@Deprecated(since = "TODO")or@Deprecated(forRemoval = true, since = "TODO"), if applicable.evalto ease future introduction of Content Security Policy (CSP) directives.Desired reviewers
@timja @mawinter69 @MarkEWaite