Class ViewStore
java.lang.Object
io.jenkins.plugins.interactiveinput.view.ViewStore
In-memory registry of
ReviewDocuments published by the interactiveView step, with
XStream persistence of the metadata to $JENKINS_HOME/interactive-input/views.xml and the
content versions stored as separate files under $JENKINS_HOME/interactive-input/views/<id>/.
Deliberately mirrors QuestionStore: a ConcurrentHashMap of documents, all
transitions on a single document serialised via synchronized (doc), a transient resolver
map that resumes a paused wait:true step when a decision is recorded, and SLA/retention
driven by the shared SlaTicker. Keeping content out of the XML (in per-version files) keeps
the metadata file small even for large reports.
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionstatic interfaceCallback invoked once when a blocking review reaches a decided (or expired) state. -
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionvoidMark an open blocking review asABORTEDwithout firing its resolver.addComment(String id, int line, String body, String byUserId) Add a root comment.Add a comment, optionally as a threaded reply and/or with a display-only author label.addComment(String id, int line, String body, String byUserId, String parentId, String authorLabel, String quote) Add a comment that also records the verbatimquotethe reviewer highlighted (see the viewer's highlight-select flow).booleanbooleanbooleancanView(ReviewDocument doc) voidcompact(long now, long retentionMs) Remove decided reviews older thanretentionMs.intcountNotificationsForBuild(String jobFullName, int buildNumber) intcountNotificationsForJob(String jobFullName) static Stringvoiddecide(String id, ReviewStatus decision, String byUserId, String source) Record a reviewer decision and, for a blocking view, resume the paused pipeline.voidexpireOverdue(long now) Expire every overdue blocking review.Job<?, ?> findJob(ReviewDocument doc) static ViewStoreget()booleanhasAnyForBuild(String jobFullName, int buildNumber) Existence probe (no permission filter) used by the run-action factory.booleanhasNotificationForBuild(String jobFullName, int buildNumber) listForBuild(String jobFullName, int buildNumber) listForJob(String jobFullName) listNotificationsForJob(String jobFullName) intmarkBuildDeletedForBuild(String jobFullName, int buildNumber) Mark every review of a now-deleted build as build-deleted.readContent(String id, int version) intStartup self-heal: mark reviews whose owning build no longer exists (deleted before this cleanup shipped, or while the controller was down).voidregister(String id, ViewStore.Resolution resolution) Attach a paused (wait:true) step's resolver.voidRemove a document outright (metadata + all content versions).intSave an edit to the durable review copy as a new content version with the default "edited" note.intSave an edit to the durable review copy as a new content version, recording an optional display-onlynote(e.g.voidsetCommentResolved(String id, String commentId, boolean resolved) Toggle a comment's resolved flag.submit(ReviewDocument doc, String content) Register a new review document and persist its metadata plus the original content snapshot.voidunregister(String id)
-
Constructor Details
-
ViewStore
public ViewStore()
-
-
Method Details
-
get
-
submit
Register a new review document and persist its metadata plus the original content snapshot.- Parameters:
doc- the document metadata (its version 1 is the original snapshot)content- the snapshotted file content (UTF-8 text)- Returns:
- the same document for convenience
-
register
Attach a paused (wait:true) step's resolver. If the document has already been decided or expired (e.g. it settled while the step was resuming after a restart), the resolver runs at once. -
unregister
-
get
-
remove
Remove a document outright (metadata + all content versions). -
addComment
@NonNull public ReviewComment addComment(@NonNull String id, int line, @NonNull String body, @NonNull String byUserId) Add a root comment. Callers must pre-check permissions (the REST/UI layer's responsibility).- Throws:
IllegalStateException- if the document is missing or not commentable
-
addComment
@NonNull public ReviewComment addComment(@NonNull String id, int line, @NonNull String body, @NonNull String byUserId, @CheckForNull String parentId, @CheckForNull String authorLabel) Add a comment, optionally as a threaded reply and/or with a display-only author label. Callers must pre-check permissions (the REST/UI layer's responsibility). ThebyUserIdaudit author is always the real, server-set identity;authorLabelonly changes what the viewer displays.Threads are kept one level deep: a reply whose
parentIdpoints at another reply is re-parented to that reply's root, so every stored reply references a root comment and the viewer can render a simple parent→replies tree.- Parameters:
parentId- the comment being replied to, ornullfor a root commentauthorLabel- display-only label (e.g. "AI response"), ornullto show the real author- Throws:
IllegalStateException- if the document is missing or not commentableIllegalArgumentException- ifparentIddoes not reference an existing comment
-
addComment
@NonNull public ReviewComment addComment(@NonNull String id, int line, @NonNull String body, @NonNull String byUserId, @CheckForNull String parentId, @CheckForNull String authorLabel, @CheckForNull String quote) Add a comment that also records the verbatimquotethe reviewer highlighted (see the viewer's highlight-select flow). Anchoring still usesline;quoteis display-only context, so a value is kept even when it is a sub-phrase of the line or spans several lines. Callers must pre-check permissions (the REST/UI layer's responsibility).- Parameters:
quote- the highlighted snippet to show back verbatim, ornullfor the "+"/general path- Throws:
IllegalStateException- if the document is missing or not commentableIllegalArgumentException- ifparentIddoes not reference an existing comment
-
setCommentResolved
Toggle a comment's resolved flag.- Throws:
IllegalStateException- if the document or comment is missing
-
saveEdit
Save an edit to the durable review copy as a new content version with the default "edited" note.- Returns:
- the new version index
- Throws:
IllegalStateException- if the document is missing, not editable, or in a final decided state (any decision other thanCHANGES_REQUESTED; seeReviewStatus.allowsEdit())
-
saveEdit
public int saveEdit(@NonNull String id, @NonNull String newContent, @NonNull String byUserId, @CheckForNull String note) Save an edit to the durable review copy as a new content version, recording an optional display-onlynote(e.g. an AI course-correction summary) in the version history. Callers must pre-check permissions and that the document is editable.- Parameters:
note- a short edit summary shown in the version dropdown, ornullfor the default label- Returns:
- the new version index
- Throws:
IllegalStateException- if the document is missing, not editable, or in a final decided state (any decision other thanCHANGES_REQUESTED; seeReviewStatus.allowsEdit())
-
decide
public void decide(@NonNull String id, @NonNull ReviewStatus decision, @NonNull String byUserId, @NonNull String source) Record a reviewer decision and, for a blocking view, resume the paused pipeline. Callers must pre-check permissions.- Throws:
IllegalStateException- if the document is missing or already decided
-
abandonForShutdown
Mark an open blocking review asABORTEDwithout firing its resolver. Used when the owning step is stopped (build aborted): the framework already delivers the abort to the pipeline, so we only need to move the document to a terminal, compactable state. -
readContent
- Returns:
- the content of a specific version as UTF-8 text, or
nullif unavailable.
-
readCurrentContent
- Returns:
- the current version's content as UTF-8 text, or
nullif unavailable.
-
listOpenReadable
- Returns:
- every OPEN review the current user can read (admin
?all=truelist).
-
listNotifications
- Returns:
- OPEN, notify-enabled reviews to surface for the current user across all jobs.
-
listNotificationsForJob
- Returns:
- OPEN, notify-enabled reviews to surface for the current user, scoped to one job.
-
countNotificationsForJob
-
countNotificationsForBuild
- Returns:
- the number of OPEN, notify-enabled reviews to surface for the current user on a single
build, honouring the user-scope switch. Drives the run-page sidebar count badge (the
per-build equivalent of
countNotificationsForJob(String)).
-
hasNotificationForBuild
- Returns:
trueif the build has an OPEN, notify-enabled review the current user should be notified of (drives the build-history badge, honouring the user-scope switch).
-
listForJob
- Returns:
- every review (any status) for
jobFullNamethe current user can read.
-
listForBuild
- Returns:
- every review (any status) for a specific build the current user can read.
-
hasAnyForBuild
Existence probe (no permission filter) used by the run-action factory. Anyone reaching a build page already holdsItem.READ, so this leaks nothing the permission-checked page would not.- Returns:
trueif any review (any status) is recorded for this build.
-
canView
- Returns:
trueif the current authentication can read the source job (Item.READ).
-
canContribute
- Returns:
trueif the current authentication may contribute (comment / edit / decide):Item.BUILDon the job, or membership in the document'ssubmitterFilter(with administrators always allowed). MirrorsQuestionStore.canAnswer(io.jenkins.plugins.interactiveinput.model.Question).
-
canContributeEffective
- Returns:
trueif the current authentication may contribute (comment / edit / decide) after applying the lock-to-build-starter switch. This iscanContribute(ReviewDocument)unless the switch is on, in which case only the build starter (or a Jenkins administrator) may contribute; reviews with no human starter are never locked. This is the check the REST mutation endpoints and the page'scanContributeflag use — it can only restrict, never widen, access. MirrorsQuestionStore.canAnswerEffective(io.jenkins.plugins.interactiveinput.model.Question), so "only the build starter may answer (others can view)" governs reviews exactly as it governs questions.
-
findJob
-
expireOverdue
public void expireOverdue(long now) Expire every overdue blocking review. Called by the SLA ticker. -
compact
public void compact(long now, long retentionMs) Remove decided reviews older thanretentionMs. Called by the SLA ticker. -
markBuildDeletedForBuild
Mark every review of a now-deleted build as build-deleted. The reviews are kept as durable audit records (their comment/decision history outlives the build), but they drop out of the notification centre and the sidebar/badge counts and the per-job page renders them as "build deleted". Invoked by theRunListenerwhen a build is deleted.- Returns:
- the number of reviews newly marked (0 if none matched or all were already marked).
-
reconcileDeletedBuilds
public int reconcileDeletedBuilds()Startup self-heal: mark reviews whose owning build no longer exists (deleted before this cleanup shipped, or while the controller was down). Only acts when the job still resolves but the build is gone, so a temporarily-unresolvable job (folder still loading, security, etc.) never loses its reviews. Invoked once fromBuildLifecycleCleanup'sonLoaded.- Returns:
- the number of reviews newly marked.
-
currentUserId
- Returns:
- the user id of the current authentication, or
"SYSTEM"if unauthenticated.
-