| 1 | // SPDX-License-Identifier: AGPL-3.0-or-later |
| 2 | |
| 3 | package policy |
| 4 | |
| 5 | // RepoRef is the policy-side projection of a repos row. Construct from |
| 6 | // either reposdb.Repo or any of the GetRepoBy* row types via NewRepoRef |
| 7 | // helpers in the policy package's adapters file (kept policy-internal |
| 8 | // so the policy package never imports reposdb directly — sqlc rows |
| 9 | // flow in via constructors at call boundaries). |
| 10 | // |
| 11 | // OwnerOrgID is the S31-shape: zero today; populated when org-owned |
| 12 | // repos ship. Can() already branches on OwnerOrgID > 0 so that S31's |
| 13 | // org membership lookup plugs in cleanly. |
| 14 | type RepoRef struct { |
| 15 | ID int64 |
| 16 | OwnerUserID int64 |
| 17 | OwnerOrgID int64 |
| 18 | Visibility string // "public" | "private" |
| 19 | IsArchived bool |
| 20 | IsDeleted bool |
| 21 | |
| 22 | // AuthorUserID is the author of the *issue or PR* being acted on, |
| 23 | // when the action is one that grants author-self privileges |
| 24 | // (`ActionIssueClose`, `ActionPullClose`). Zero means "no author |
| 25 | // context" — e.g. a repo-level read or write — and is the default. |
| 26 | // Handlers populate this only on the close paths; everywhere else |
| 27 | // it stays zero. |
| 28 | // |
| 29 | // This is a pragmatic v1 shape: instead of introducing an |
| 30 | // IssueRef/PullRef parallel to RepoRef, we widen RepoRef with the |
| 31 | // one fact the close gate needs. When/if more issue-level rules |
| 32 | // land (assignee privileges, labeler privileges) we'll graduate |
| 33 | // to a proper IssueRef. The audit captured the design tradeoff; |
| 34 | // don't add new fields here without re-reading that note. |
| 35 | AuthorUserID int64 |
| 36 | } |
| 37 | |
| 38 | // IsPublic returns true when the repo's visibility column is "public". |
| 39 | // Other code paths must not parse Visibility directly — read it through |
| 40 | // these helpers so the canonical strings live in one place. |
| 41 | func (r RepoRef) IsPublic() bool { return r.Visibility == "public" } |
| 42 | |
| 43 | // IsPrivate is the inverse. Use whichever phrasing reads better at the |
| 44 | // call site. |
| 45 | func (r RepoRef) IsPrivate() bool { return r.Visibility == "private" } |
| 46 | |
| 47 | // IsOwnedByUser reports whether userID is the direct user owner. Keep |
| 48 | // ownership interpretation here so handlers do not grow inline owner |
| 49 | // checks next to request behavior. |
| 50 | func (r RepoRef) IsOwnedByUser(userID int64) bool { |
| 51 | return userID != 0 && r.OwnerUserID == userID |
| 52 | } |
| 53 |