Skip to content

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

254 linesCodeBlameRaw
1//! Work items (`g1t_contracts::work::items`): any tracked work of a
2//! workspace, linked to the repositories it concerns
3//! (migrations/0049_work_items.sql).
4//!
5//! A work item is numbered in its workspace's sequence (`work_counters`,
6//! numbering.rs), which new pull requests share, and links repositories in
7//! `work_item_repos` (links.rs): its origin, its targets and those it
8//! touched. Who may see one is `access::item_role`: the workspace's
9//! members, and anyone who can read its origin. What `#N` names is
10//! refs.rs; what landing one does, landing.rs. Labels and milestones are
11//! the workspace's, and pull requests carry them too (labels.rs,
12//! milestones.rs). Moving repositories' issues in is migration.rs.
13//!
14//! This module is the boundary. Its RPC methods (`open_work_item` and the
15//! other `*_work_item` methods, labels, milestones, `resolve_ref`,
16//! `closed_by_commit`, `legacy_item_map`, `migrate_work_items`) are the
17//! service's; the rest of the crate (pull requests, merging, pushes,
18//! coordination) reaches work items only through the functions below, and
19//! reads them in its own queries only through the fragments in [`sql`]. A
20//! later split of work items into a service of their own turns each
21//! function into an RPC method and each fragment into a copy the caller
22//! keeps from `work_item.*` events.
23//!
24//! Work items call out the other way at a few points, which a split would
25//! turn into events: what a close means for claims and blocked work
26//! (`Work::item_resolved`), the session a close was made in
27//! (`Work::caller_session`, `Work::trailer_session`), and repositories and
28//! their settings. `repo_workspaces`, where each repository is, is kept
29//! here and read by anyone.
30
31use g1t_contracts::access::Capability;
32use g1t_contracts::repos::Repo;
33use g1t_contracts::work::refs::Ref;
34use g1t_contracts::{Outcome, User, Viewer};
35use worker::Result;
36
37use crate::Work;
38
39/// Unwraps an `Outcome`, returning its failure from the enclosing method.
40macro_rules! check {
41 ($outcome:expr) => {
42 match $outcome {
43 Outcome::Ok(value) => value,
44 Outcome::Fail(failure) => return Ok(Outcome::Fail(failure)),
45 }
46 };
47}
48
49mod changes;
50mod dependencies;
51mod labels;
52mod landing;
53mod links;
54mod migration;
55mod milestones;
56mod numbering;
57mod reading;
58mod refs;
59mod rows;
60pub(crate) mod sql;
61#[cfg(test)]
62mod tests;
63
64pub(crate) use labels::Labeled;
65pub(crate) use landing::{Landing, PastLanding};
66pub(crate) use migration::{DELETED, RENAMED, TRANSFERRED};
67pub(crate) use reading::{held_rows, select};
68pub(crate) use refs::{MAX_CLOSES, Named, NamedItems};
69pub(crate) use rows::{Held, path_of, repo_readable, workspace_of};
70
71/// Work items and people made up for other modules' tests.
72#[cfg(test)]
73pub(crate) mod rows_for_test {
74 pub(crate) use super::rows::{held_for_test, person_for_test};
75}
76
77// --- Reading ---------------------------------------------------------------
78
79/// The work item numbered `number` in `workspace`, whoever is asking.
80pub(crate) async fn by_number(work: &Work, workspace: &str, number: u32) -> Result<Option<Held>> {
81 work.item(workspace, number).await
82}
83
84/// A work item by id, whoever is asking.
85pub(crate) async fn by_id(work: &Work, id: &str) -> Result<Option<Held>> {
86 work.item_by_id(id).await
87}
88
89/// The work items of these ids, in no particular order.
90pub(crate) async fn by_ids(work: &Work, ids: &[String]) -> Result<Vec<Held>> {
91 work.items_by_ids(ids).await
92}
93
94/// A work item, if `viewer` may do `capability` on it; not found when they
95/// may not see it.
96pub(crate) async fn for_viewer(work: &Work, viewer: &Viewer, workspace: &str, number: u32, capability: Capability) -> Result<Outcome<Held>> {
97 work.item_for(viewer, workspace, number, capability).await
98}
99
100/// A pull request's own work item, if it has one.
101pub(crate) async fn for_pull(work: &Work, pull_id: &str) -> Result<Option<Held>> {
102 match work.pull_item_id(pull_id).await? {
103 Some(id) => work.item_by_id(&id).await,
104 None => Ok(None),
105 }
106}
107
108// --- Numbers and repositories ----------------------------------------------
109
110/// The next number of `workspace`'s sequence, for a pull request of
111/// `repo_id` or (without one) a work item.
112pub(crate) async fn next_number(work: &Work, workspace: &str, repo_id: Option<&str>) -> Result<u32> {
113 work.next_number(workspace, repo_id).await
114}
115
116/// Records which workspace `repo` is in, its path and whether it is
117/// private, as repos just said.
118pub(crate) async fn remember_repo(work: &Work, repo: &Repo) -> Result<()> {
119 work.remember_repo(repo).await
120}
121
122/// The workspace repository `repo_id` is in.
123pub(crate) async fn repo_workspace(work: &Work, repo_id: &str) -> Result<Option<String>> {
124 work.repo_workspace(repo_id).await
125}
126
127/// `repo.created`: where the repository is, and for a workspace's first,
128/// the default labels.
129pub(crate) async fn repo_created(work: &Work, data: &serde_json::Value) -> Result<()> {
130 work.repo_created(data).await
131}
132
133/// `repo.restored`: where the repository is again, and what it kept for
134/// itself, placed.
135pub(crate) async fn repo_restored(work: &Work, data: &serde_json::Value) -> Result<()> {
136 work.repo_restored(data).await
137}
138
139/// `repo.visibility_changed`: whether its work items are public.
140pub(crate) async fn repo_visibility(work: &Work, data: &serde_json::Value) -> Result<()> {
141 work.repo_visibility(data).await
142}
143
144// --- References and landing ------------------------------------------------
145
146/// What `reference` names, read in repository `repo_id` of `workspace`,
147/// with the repository it is read in (another of the workspace's, for
148/// `owner/name#N`): none for another workspace's, or a repository not
149/// known.
150pub(crate) async fn resolve_in(work: &Work, repo_id: &str, workspace: &str, reference: &Ref) -> Result<Option<(String, Named)>> {
151 work.resolve_in(repo_id, workspace, reference).await
152}
153
154/// The work item `reference` names, read in `repo`, by id: one of its
155/// workspace's, or none.
156pub(crate) async fn referenced(work: &Work, repo: &Repo, reference: &Ref) -> Result<Option<String>> {
157 work.ref_item(repo, reference).await
158}
159
160/// The work items `text`, written in `repo`, closes and is part of, with
161/// `own` (a pull request's own work item) closed first: those each of
162/// `who` may close, or see (refs.rs `admits`).
163pub(crate) async fn named(work: &Work, repo: &Repo, own: Option<&str>, text: &str, who: &[&User]) -> Result<NamedItems> {
164 work.named_items(repo, own, text, who).await
165}
166
167/// Whether each of `who` may have `held` closed by naming it (`closing`),
168/// or linked as something a change is part of.
169pub(crate) fn admits(held: &Held, who: &[&User], closing: bool) -> bool {
170 refs::admits(held, who, closing)
171}
172
173/// Lands `closes` (work items, by id) in `repo` by `landing` and links
174/// `parts` (landing.rs). Returns the work items it closed.
175#[allow(clippy::too_many_arguments)]
176pub(crate) async fn land(
177 work: &Work,
178 repo: &Repo,
179 landing: &Landing,
180 actor: &User,
181 closes: &[String],
182 parts: &[String],
183 session: Option<&str>,
184 agent: bool,
185) -> Result<Vec<Held>> {
186 work.land(repo, landing, actor, closes, parts, session, agent).await
187}
188
189/// Links `repo` to `held` as touched (it has a pull request for it),
190/// unless it is linked already.
191pub(crate) async fn touch(work: &Work, held: &Held, repo: &Repo, actor: &User) -> Result<()> {
192 work.touch_item(held, repo, actor).await
193}
194
195/// Where the other work items of `held`'s workspace landed, newest first.
196pub(crate) async fn landed_before(work: &Work, held: &Held, limit: u32) -> Result<Vec<PastLanding>> {
197 work.landed_before(held, limit).await
198}
199
200// --- Its conversation ------------------------------------------------------
201
202/// Notes something that happened to a work item, in its conversation.
203pub(crate) async fn note<'a>(work: &Work, item_id: &str, author: impl Into<crate::rows::By<'a>>, text: &str) -> Result<()> {
204 work.note_item(item_id, author, text).await
205}
206
207/// A statement that marks a work item updated at `at`, for a caller's
208/// batch (a comment on it).
209pub(crate) fn stamp(work: &Work, item_id: &str, at: &str) -> Result<worker::D1PreparedStatement> {
210 work.db.prepare("UPDATE work_items SET updated_at = ? WHERE id = ?").bind(&[at.into(), item_id.into()])
211}
212
213// --- Labels and milestones -------------------------------------------------
214
215/// Sets the labels of a work item or pull request to `wanted`.
216pub(crate) async fn relabel(work: &Work, actor: &User, labeled: &Labeled, wanted: &[String]) -> Result<Outcome<Vec<String>>> {
217 work.relabel(actor, labeled, wanted).await
218}
219
220/// Puts a work item or pull request in milestone `number` (0: none).
221pub(crate) async fn set_milestone(work: &Work, actor: &User, labeled: &Labeled, number: u32) -> Result<Outcome<()>> {
222 work.set_milestone(actor, labeled, number).await
223}
224
225// --- What blocks what ------------------------------------------------------
226
227/// The open work items `held` blocks.
228pub(crate) async fn dependents(work: &Work, held: &Held) -> Result<Vec<Held>> {
229 work.dependents(held).await
230}
231
232/// Those of `numbers` whose work item in `workspace` is still open,
233/// leaving out `cleared`.
234pub(crate) async fn still_open(work: &Work, workspace: &str, numbers: &[u32], cleared: Option<u32>) -> Result<Vec<u32>> {
235 work.still_open(workspace, numbers, cleared).await
236}
237
238/// Those of `ids` (repositories) that `viewer` may read, as recorded in
239/// `repo_workspaces`.
240pub(crate) async fn readable_repo_ids(work: &Work, viewer: Option<&User>, ids: &[String]) -> Result<std::collections::HashSet<String>> {
241 work.readable_repo_ids(viewer, ids).await
242}
243
244/// The work items of `workspace` that `numbers` names and `viewer` may
245/// see, in no particular order.
246pub(crate) async fn visible_numbered(work: &Work, workspace: &str, numbers: &[u32], viewer: Option<&User>) -> Result<Vec<Held>> {
247 work.visible_numbered(workspace, numbers, viewer).await
248}
249
250/// The work items that name `held` in their `blocked_by`, those `viewer`
251/// may see.
252pub(crate) async fn blocked_items(work: &Work, held: &Held, viewer: Option<&User>) -> Result<Vec<g1t_contracts::work::ItemBrief>> {
253 work.blocked_items(held, viewer).await
254}