Module: PlanMyStuff::IssueExtractions::Links
- Included in:
- PlanMyStuff::Issue
- Defined in:
- lib/plan_my_stuff/issue_extractions/links.rb
Instance Method Summary collapse
-
#add_blocker!(target) ⇒ PlanMyStuff::Link
Records that
targetblocks self. -
#add_related!(target, user: nil, reciprocal: false) ⇒ PlanMyStuff::Link
Adds a
:relatedlink totargetand, unless this call is already a reciprocal, mirrors the link back ontargetso the pairing is symmetric. -
#add_sub_issue!(target) ⇒ PlanMyStuff::Link
Adds
targetas a sub-issue of self via POST /issues/{number}/sub_issues. -
#blocked_by ⇒ Array<PlanMyStuff::Issue>
Lazy-memoized issues that block self (i.e. self is blocked by each returned issue) via GitHub’s native issue-dependency REST API.
-
#blocking ⇒ Array<PlanMyStuff::Issue>
Lazy-memoized issues that self blocks.
-
#duplicate_of ⇒ PlanMyStuff::Issue?
Lazy-memoized issue that self was marked as duplicate of, via GitHub’s native close-as-duplicate.
-
#mark_duplicate!(target, user: nil) ⇒ PlanMyStuff::Link
Closes self as a duplicate of
targetvia GitHub’s native close-as-duplicate, carrying over viewers, assignees, and a back-pointer comment on the target. -
#parent ⇒ PlanMyStuff::Issue?
Lazy-memoized parent issue via GitHub’s native sub-issues API.
-
#related ⇒ Array<PlanMyStuff::Issue>
Lazy-memoized array of
Issueobjects for:relatedlinks. -
#remove_blocker!(target) ⇒ PlanMyStuff::Link
Removes the record that
targetblocks self. -
#remove_parent! ⇒ PlanMyStuff::Link?
Detaches self from its current parent, if any.
-
#remove_related!(target, user: nil, reciprocal: false) ⇒ PlanMyStuff::Link
Removes a
:relatedlink totargetand, unless this call is already a reciprocal, mirrors the removal ontarget. -
#remove_sub_issue!(target) ⇒ PlanMyStuff::Link
Removes
targetas a sub-issue of self via DELETE /issues/{number}/sub_issue (singular). -
#set_parent!(target) ⇒ PlanMyStuff::Link
Makes
targetthe parent of self. -
#sub_tickets ⇒ Array<PlanMyStuff::Issue>
Lazy-memoized sub-issues via GitHub’s native sub-issues API.
Instance Method Details
#add_blocker!(target) ⇒ PlanMyStuff::Link
Records that target blocks self. Native GitHub action; notifications are handled by GitHub itself.
157 158 159 160 161 162 163 164 165 166 167 168 169 |
# File 'lib/plan_my_stuff/issue_extractions/links.rb', line 157 def add_blocker!(target) link = build_link!(target, type: :blocked_by) validate_not_self!(link) target_issue = resolve_target_issue(target, type: :blocked_by) PlanMyStuff.client.rest( :post, dependency_path('blocked_by'), { issue_id: target_issue.__send__(:require_github_id!) }, ) invalidate_links_cache! link end |
#add_related!(target, user: nil, reciprocal: false) ⇒ PlanMyStuff::Link
Adds a :related link to target and, unless this call is already a reciprocal, mirrors the link back on target so the pairing is symmetric. Dedups on (type, issue_number, repo) - re-adding is a no-op.
24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
# File 'lib/plan_my_stuff/issue_extractions/links.rb', line 24 def (target, user: nil, reciprocal: false) link = build_link!(target, type: :related) validate_not_self!(link) existing = current_links return link if existing.include?(link) persist_links!(existing + [link]) unless reciprocal mirror_on_target(link, user: user) { |other| other.(self, user: user, reciprocal: true) } end link end |
#add_sub_issue!(target) ⇒ PlanMyStuff::Link
Adds target as a sub-issue of self via POST /issues/{number}/sub_issues. Native GitHub action; notifications are handled by GitHub itself.
88 89 90 |
# File 'lib/plan_my_stuff/issue_extractions/links.rb', line 88 def add_sub_issue!(target) mutate_sub_issue!(target, method: :post, path: sub_issues_path) end |
#blocked_by ⇒ Array<PlanMyStuff::Issue>
Lazy-memoized issues that block self (i.e. self is blocked by each returned issue) via GitHub’s native issue-dependency REST API.
139 140 141 |
# File 'lib/plan_my_stuff/issue_extractions/links.rb', line 139 def blocked_by links_cache[:blocked_by] ||= fetch_dependencies('blocked_by') end |
#blocking ⇒ Array<PlanMyStuff::Issue>
Lazy-memoized issues that self blocks.
147 148 149 |
# File 'lib/plan_my_stuff/issue_extractions/links.rb', line 147 def blocking links_cache[:blocking] ||= fetch_dependencies('blocking') end |
#duplicate_of ⇒ PlanMyStuff::Issue?
Lazy-memoized issue that self was marked as duplicate of, via GitHub’s native close-as-duplicate. Returns nil for issues that are open or closed for other reasons.
195 196 197 198 199 |
# File 'lib/plan_my_stuff/issue_extractions/links.rb', line 195 def duplicate_of return links_cache[:duplicate_of] if links_cache.key?(:duplicate_of) links_cache[:duplicate_of] = fetch_duplicate_of end |
#mark_duplicate!(target, user: nil) ⇒ PlanMyStuff::Link
Closes self as a duplicate of target via GitHub’s native close-as-duplicate, carrying over viewers, assignees, and a back-pointer comment on the target.
Side effects, in order:
-
Resolves
target; raisesValidationErrorif missing. -
Raises
ValidationErrorwhen self is already closed. -
Merges self’s
visibility_allowlistonto target. -
Merges self’s assignees onto target.
-
Posts a PMS comment on target with the back-pointer.
-
Closes self with state_reason: :duplicate and duplicate_of: { owner:, repo:, number: }.
-
Reloads self; invalidates link caches.
-
Fires
plan_my_stuff.issue.marked_duplicate.
Partial failures are not rolled back - GitHub retains whatever side effects succeeded before the failing step.
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 |
# File 'lib/plan_my_stuff/issue_extractions/links.rb', line 224 def mark_duplicate!(target, user: nil) raise(PlanMyStuff::ValidationError, 'Cannot mark a closed issue as duplicate') if state == 'closed' target_issue = resolve_duplicate_target!(target) merge_visibility_allowlist_onto!(target_issue) merge_assignees_onto!(target_issue) post_duplicate_back_pointer!(target_issue, user: user) close_as_duplicate!(target_issue) reload invalidate_links_cache! PlanMyStuff::Notifications.instrument('issue.marked_duplicate', self, target: target_issue, user: user) build_link!(target_issue, type: :duplicate_of) end |
#parent ⇒ PlanMyStuff::Issue?
Lazy-memoized parent issue via GitHub’s native sub-issues API. GitHub enforces at most one parent per issue.
67 68 69 70 71 |
# File 'lib/plan_my_stuff/issue_extractions/links.rb', line 67 def parent return links_cache[:parent] if links_cache.key?(:parent) links_cache[:parent] = fetch_parent end |
#related ⇒ Array<PlanMyStuff::Issue>
Lazy-memoized array of Issue objects for :related links. Silently drops targets that 404 so a dangling pointer doesn’t break the rest of the list.
11 12 13 |
# File 'lib/plan_my_stuff/issue_extractions/links.rb', line 11 def links_cache[:related] ||= end |
#remove_blocker!(target) ⇒ PlanMyStuff::Link
Removes the record that target blocks self.
177 178 179 180 181 182 183 184 185 186 187 188 |
# File 'lib/plan_my_stuff/issue_extractions/links.rb', line 177 def remove_blocker!(target) link = build_link!(target, type: :blocked_by) validate_not_self!(link) target_issue = resolve_target_issue(target, type: :blocked_by) PlanMyStuff.client.rest( :delete, "#{dependency_path('blocked_by')}/#{target_issue.__send__(:require_github_id!)}", ) invalidate_links_cache! link end |
#remove_parent! ⇒ PlanMyStuff::Link?
Detaches self from its current parent, if any. Returns the Link that was removed, or nil when there was no parent.
124 125 126 127 128 129 130 131 132 |
# File 'lib/plan_my_stuff/issue_extractions/links.rb', line 124 def remove_parent! current = parent return if current.nil? current.remove_sub_issue!(self) invalidate_links_cache! build_link!(current, type: :parent) end |
#remove_related!(target, user: nil, reciprocal: false) ⇒ PlanMyStuff::Link
Removes a :related link to target and, unless this call is already a reciprocal, mirrors the removal on target. No-op when the link isn’t present locally.
48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
# File 'lib/plan_my_stuff/issue_extractions/links.rb', line 48 def (target, user: nil, reciprocal: false) link = build_link!(target, type: :related) validate_not_self!(link) existing = current_links return link if existing.exclude?(link) persist_links!(existing.reject { |l| l == link }) unless reciprocal mirror_on_target(link, user: user) { |other| other.(self, user: user, reciprocal: true) } end link end |
#remove_sub_issue!(target) ⇒ PlanMyStuff::Link
Removes target as a sub-issue of self via DELETE /issues/{number}/sub_issue (singular).
98 99 100 |
# File 'lib/plan_my_stuff/issue_extractions/links.rb', line 98 def remove_sub_issue!(target) mutate_sub_issue!(target, method: :delete, path: remove_sub_issue_path) end |
#set_parent!(target) ⇒ PlanMyStuff::Link
Makes target the parent of self. If self already has a parent, it is detached first. Returns a Link describing the new :parent relationship.
109 110 111 112 113 114 115 116 117 |
# File 'lib/plan_my_stuff/issue_extractions/links.rb', line 109 def set_parent!(target) parent.presence&.remove_sub_issue!(self) target_issue = resolve_target_issue(target, type: :parent) target_issue.add_sub_issue!(self) invalidate_links_cache! build_link!(target_issue, type: :parent) end |
#sub_tickets ⇒ Array<PlanMyStuff::Issue>
Lazy-memoized sub-issues via GitHub’s native sub-issues API.
77 78 79 |
# File 'lib/plan_my_stuff/issue_extractions/links.rb', line 77 def sub_tickets links_cache[:sub_tickets] ||= fetch_sub_tickets end |