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.

251 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 `#number` names, read in repository `repo_id` of `workspace`.
147pub(crate) async fn resolve(work: &Work, repo_id: &str, workspace: &str, number: u32) -> Result<Option<Named>> {
148 Ok(work.lookup_ref(Some(repo_id), workspace, number).await?.named(true))
149}
150
151/// The work item `reference` names, read in `repo`, by id: one of its
152/// workspace's, or none.
153pub(crate) async fn referenced(work: &Work, repo: &Repo, reference: &Ref) -> Result<Option<String>> {
154 work.ref_item(repo, reference).await
155}
156
157/// The work items `text`, written in `repo`, closes and is part of, with
158/// `own` (a pull request's own work item) closed first: those each of
159/// `who` may close, or see (refs.rs `admits`).
160pub(crate) async fn named(work: &Work, repo: &Repo, own: Option<&str>, text: &str, who: &[&User]) -> Result<NamedItems> {
161 work.named_items(repo, own, text, who).await
162}
163
164/// Whether each of `who` may have `held` closed by naming it (`closing`),
165/// or linked as something a change is part of.
166pub(crate) fn admits(held: &Held, who: &[&User], closing: bool) -> bool {
167 refs::admits(held, who, closing)
168}
169
170/// Lands `closes` (work items, by id) in `repo` by `landing` and links
171/// `parts` (landing.rs). Returns the work items it closed.
172#[allow(clippy::too_many_arguments)]
173pub(crate) async fn land(
174 work: &Work,
175 repo: &Repo,
176 landing: &Landing,
177 actor: &User,
178 closes: &[String],
179 parts: &[String],
180 session: Option<&str>,
181 agent: bool,
182) -> Result<Vec<Held>> {
183 work.land(repo, landing, actor, closes, parts, session, agent).await
184}
185
186/// Links `repo` to `held` as touched (it has a pull request for it),
187/// unless it is linked already.
188pub(crate) async fn touch(work: &Work, held: &Held, repo: &Repo, actor: &User) -> Result<()> {
189 work.touch_item(held, repo, actor).await
190}
191
192/// Where the other work items of `held`'s workspace landed, newest first.
193pub(crate) async fn landed_before(work: &Work, held: &Held, limit: u32) -> Result<Vec<PastLanding>> {
194 work.landed_before(held, limit).await
195}
196
197// --- Its conversation ------------------------------------------------------
198
199/// Notes something that happened to a work item, in its conversation.
200pub(crate) async fn note<'a>(work: &Work, item_id: &str, author: impl Into<crate::rows::By<'a>>, text: &str) -> Result<()> {
201 work.note_item(item_id, author, text).await
202}
203
204/// A statement that marks a work item updated at `at`, for a caller's
205/// batch (a comment on it).
206pub(crate) fn stamp(work: &Work, item_id: &str, at: &str) -> Result<worker::D1PreparedStatement> {
207 work.db.prepare("UPDATE work_items SET updated_at = ? WHERE id = ?").bind(&[at.into(), item_id.into()])
208}
209
210// --- Labels and milestones -------------------------------------------------
211
212/// Sets the labels of a work item or pull request to `wanted`.
213pub(crate) async fn relabel(work: &Work, actor: &User, labeled: &Labeled, wanted: &[String]) -> Result<Outcome<Vec<String>>> {
214 work.relabel(actor, labeled, wanted).await
215}
216
217/// Puts a work item or pull request in milestone `number` (0: none).
218pub(crate) async fn set_milestone(work: &Work, actor: &User, labeled: &Labeled, number: u32) -> Result<Outcome<()>> {
219 work.set_milestone(actor, labeled, number).await
220}
221
222// --- What blocks what ------------------------------------------------------
223
224/// The open work items `held` blocks.
225pub(crate) async fn dependents(work: &Work, held: &Held) -> Result<Vec<Held>> {
226 work.dependents(held).await
227}
228
229/// Those of `numbers` whose work item in `workspace` is still open,
230/// leaving out `cleared`.
231pub(crate) async fn still_open(work: &Work, workspace: &str, numbers: &[u32], cleared: Option<u32>) -> Result<Vec<u32>> {
232 work.still_open(workspace, numbers, cleared).await
233}
234
235/// Those of `ids` (repositories) that `viewer` may read, as recorded in
236/// `repo_workspaces`.
237pub(crate) async fn readable_repo_ids(work: &Work, viewer: Option<&User>, ids: &[String]) -> Result<std::collections::HashSet<String>> {
238 work.readable_repo_ids(viewer, ids).await
239}
240
241/// The work items of `workspace` that `numbers` names and `viewer` may
242/// see, in no particular order.
243pub(crate) async fn visible_numbered(work: &Work, workspace: &str, numbers: &[u32], viewer: Option<&User>) -> Result<Vec<Held>> {
244 work.visible_numbered(workspace, numbers, viewer).await
245}
246
247/// The work items that name `held` in their `blocked_by`, those `viewer`
248/// may see.
249pub(crate) async fn blocked_items(work: &Work, held: &Held, viewer: Option<&User>) -> Result<Vec<g1t_contracts::work::ItemBrief>> {
250 work.blocked_items(held, viewer).await
251}