1// SPDX-License-Identifier: AGPL-3.0-or-later
2
3package ports
4
5import (
6 "time"
7
8 "bnema.dev/ermite/internal/domain"
9)
10
11// The parameters and results of the use case interfaces (services.go). They
12// live here so that the adapters that call the use cases and the use cases
13// themselves share them without importing each other.
14
15// RepoPage is a page of repositories ordered by name.
16type RepoPage struct {
17 Repos []domain.Repo
18 // NextPageToken is empty on the last page.
19 NextPageToken string
20}
21
22// RepoView is a repository with the Git state that belongs in its summary.
23type RepoView struct {
24 domain.Repo // includes ActiveAt, the latest repository activity
25 // DefaultBranch is the branch HEAD points to, even when it does not exist
26 // yet; empty when HEAD is detached or the directory is missing.
27 DefaultBranch string
28}
29
30// CreateRepoParams are the settings of a new repository.
31type CreateRepoParams struct {
32 Name domain.RepoName
33 Description string
34 // Topics are normalized by Create (see domain.NormalizeTopics).
35 Topics []string
36 // Visibility defaults to private when empty.
37 Visibility domain.Visibility
38}
39
40// UpdateRepoParams are the changes of Update; a nil field is left alone.
41type UpdateRepoParams struct {
42 Description *string
43 // Topics replaces the topics; an empty list clears them.
44 Topics *[]string
45 Visibility *domain.Visibility
46 DefaultBranch *string
47 // AllowedMergeModes replaces the allowed merge modes; it must hold at
48 // least one known mode.
49 AllowedMergeModes *[]domain.MergeMode
50 // Issues replaces the issues mode.
51 Issues *domain.IssuesMode
52 // AllowAutoMerge replaces the setting of that name.
53 AllowAutoMerge *bool
54}
55
56// EnableAutoMergeParams are the settings of EnableAutoMerge.
57type EnableAutoMergeParams struct {
58 // Mode is the merge mode to use; empty means fast-forward.
59 Mode domain.MergeMode
60 // HeadID is the head the caller saw; required. Auto-merge is enabled only
61 // if the source branch still points to it.
62 HeadID domain.ObjectID
63}
64
65// TokenPage is a page of stored tokens, newest first.
66type TokenPage struct {
67 Tokens []domain.Token
68 // NextPageToken is empty on the last page.
69 NextPageToken string
70}
71
72// CreateTokenParams are the settings of a new token.
73type CreateTokenParams struct {
74 Label string
75 // Scopes must not be empty.
76 Scopes []domain.Scope
77 // Repos limits the token to these repositories; empty means every one.
78 Repos []domain.RepoName
79 // BranchPrefixes limits the token to the branches under these prefixes
80 // (see domain.ParseBranchPrefix); empty means every branch.
81 BranchPrefixes []string
82 // ExpiresAt is the zero time for a token that never expires.
83 ExpiresAt time.Time
84}
85
86// CreatedToken is a new token with its secret, which is shown once and never
87// stored.
88type CreatedToken struct {
89 Token domain.Token
90 Secret string
91}
92
93// ListRefsParams selects the refs to list.
94type ListRefsParams struct {
95 // Kinds restricts the result; empty means every kind.
96 Kinds []domain.RefKind
97 // PageSize is 0 for the default, and lowered to MaxRefPageSize.
98 PageSize int
99 // PageToken is the NextPageToken of the previous page, or empty.
100 PageToken string
101}
102
103// RefPage is a page of refs.
104type RefPage struct {
105 Refs []domain.Ref
106 // NextPageToken is empty on the last page.
107 NextPageToken string
108}
109
110// ListCommitsParams selects a page of history.
111type ListCommitsParams struct {
112 // Revision is where the history starts; the zero value is HEAD. Ignored
113 // when PageToken is set: the token pins the commit the first page started
114 // from, so every page walks the same history even if a branch moves.
115 Revision domain.Revision
116 // Path limits the history to the commits that changed it. It must be the
117 // same on every page of a listing.
118 Path domain.TreePath
119 // PageSize is 0 for the default, and lowered to MaxPageSize.
120 PageSize int
121 // PageToken is the NextPageToken of the previous page, or empty.
122 PageToken string
123}
124
125// HistoryPage is a page of history, newest first.
126type HistoryPage struct {
127 Commits []domain.Commit
128 // NextPageToken is empty on the last page.
129 NextPageToken string
130}
131
132// RepoStats are the figures of a repository the home page shows.
133type RepoStats struct {
134 // Commits is the number of commits of the default branch; Recent of them
135 // were committed in the last RecentDays days.
136 Commits, Recent int
137 // LatestTag is the most recently created tag; nil without tags.
138 LatestTag *domain.TagInfo
139 // Activity is the number of commits of each of the last ActivityDays
140 // days, oldest first; the last day ends now. A day is 24 hours.
141 Activity []int
142 // Partial tells that the recent commits were more than MaxCommitTimes:
143 // Recent and Activity count only some of them.
144 Partial bool
145}
146
147// RecentDays is the window of RepoStats.Recent.
148const RecentDays = 30
149
150// ActivityDays is the span of RepoStats.Activity.
151const ActivityDays = 90
152
153// WatchParams select the events of a watch.
154type WatchParams struct {
155 // Topics are TopicHome or "repo:<name>"; empty means TopicHome.
156 Topics []string
157 // ResumeAfter is the id of the last event the caller saw; empty to
158 // receive only new events.
159 ResumeAfter string
160 // Client identifies where the watch comes from, for the limit per client
161 // (MaxWatchersPerClient): an opaque key such as a normalized address; the
162 // web passes its visitor's. Empty only for callers with no client, which
163 // count against MaxWatchers alone.
164 Client string
165 // Ready, when not nil, is called once the topics are valid and the watch
166 // is registered: events published from then on are not missed. Nothing is
167 // sent before it returns.
168 Ready func()
169}
170
171// WatchEvent is one item of a watch.
172type WatchEvent struct {
173 // ID identifies the event, for resuming. Empty on a gap.
174 ID string
175 // Gap tells that events were lost: the caller must reload what it shows.
176 // Event is nil then.
177 Gap bool
178 // Event is a domain.RepoCreated, RepoUpdated, RepoDeleted, PushEvent,
179 // MergeRequestOpened, MergeRequestMerged, MergeRequestClosed,
180 // MergeRequestReady, CommentAdded, RunUpdated or JobUpdated.
181 Event domain.Event
182 // Repo is the state of the repository at delivery, for RepoCreated and
183 // RepoUpdated.
184 Repo *RepoView
185}
186
187// CreateMergeRequestParams describes the merge request to open.
188type CreateMergeRequestParams struct {
189 Title string
190 Description string
191 // Source is required; Target defaults to the default branch.
192 Source, Target string
193}
194
195// ListMergeRequestsParams selects a page of merge requests.
196type ListMergeRequestsParams struct {
197 // State restricts the result; empty means every state.
198 State domain.MRState
199 // PageSize is 0 for the default, and lowered to MaxPageSize.
200 PageSize int
201 // PageToken is the NextPageToken of the previous page, or empty.
202 PageToken string
203}
204
205// MergeRequestPage is a page of merge requests, highest number first.
206type MergeRequestPage struct {
207 MergeRequests []domain.MergeRequest
208 // NextPageToken is empty on the last page.
209 NextPageToken string
210 // Mergeability holds, by number, the mergeability of the open requests of
211 // the page (nil when the server does not compute it).
212 Mergeability map[int64]domain.Mergeability
213}
214
215// ListIssuesParams selects a page of issues from an IssueStore.
216type ListIssuesParams struct {
217 // State restricts the result; empty means every state.
218 State domain.IssueState
219 // PageSize is the most issues returned; 0 or less means no limit. The
220 // use case chooses it.
221 PageSize int
222 // PageToken is the NextPageToken of the previous page, or empty.
223 PageToken string
224}
225
226// IssuePage is a page of issues, highest number first.
227type IssuePage struct {
228 Issues []domain.Issue
229 // NextPageToken is empty on the last page.
230 NextPageToken string
231}
232
233// ListPendingParams selects a page of pending issues.
234type ListPendingParams struct {
235 // PageSize is the page size; the use case applies the default and the
236 // maximum, a store takes it as is (0: every one).
237 PageSize int
238 // PageToken is the NextPageToken of the previous page.
239 PageToken string
240}
241
242// PendingIssuePage is a page of pending issues, oldest first.
243type PendingIssuePage struct {
244 Issues []domain.PendingIssue
245 NextPageToken string
246}
247
248// CreateIssueParams describes the issue to open.
249type CreateIssueParams struct {
250 Title string
251 Body string
252}
253
254// IssueReference is a merge request of the same repository naming an issue.
255type IssueReference struct {
256 Number int64
257 Title domain.MRTitle
258 State domain.MRState
259 Closes bool
260}
261
262// IssueDetail is an issue with its comments and referencing merge requests.
263type IssueDetail struct {
264 // Events are oldest first.
265 Events []IssueEventDetail
266 // References are ascending by number, at most 100.
267 References []IssueReference
268 Issue domain.Issue
269 // Comments are oldest first.
270 Comments []domain.IssueComment
271}
272
273// CompareResult describes source commits absent from target, newest first.
274type CompareResult struct {
275 Ahead []domain.Commit
276 Behind bool
277 Exact bool
278}
279
280// PushSummary counts a bounded range; More means additional commits exist.
281type PushSummary struct {
282 Count int
283 More bool
284 Rewritten bool
285}
286
287// MergeRequestDetail is a merge request with its review data.
288type MergeRequestDetail struct {
289 // Issues are current links ascending by number, at most 100; empty when issues are off.
290 Issues []MergeRequestIssue
291 // IssueEvents are append-only history, including links since removed.
292 IssueEvents []IssueEventDetail
293 MergeRequest domain.MergeRequest
294 // NewCommits is the unread prefix of Commits; zero if the read head is absent.
295 NewCommits int
296 // HeadUnread reports whether the nonzero head differs from the shared read position.
297 HeadUnread bool
298 // Commits are the commits of the head that the target lacks, newest
299 // first, at most MaxMergeRequestCommits.
300 Commits []domain.Commit
301 CommitsTruncated bool
302 // DiffBaseID is the commit the diff compares from; zero when the source
303 // branch is gone, then the diff is empty.
304 DiffBaseID domain.ObjectID
305 Stats domain.DiffStats
306 Diff domain.Diff
307 // Comments are the comments, oldest first.
308 Comments []domain.Comment
309 // Verdicts are the latest verdict per author in note ID order.
310 Verdicts []domain.AuthorVerdict
311 // Mergeability is zero unless the request is open.
312 Mergeability domain.Mergeability
313 // LastReadID is the head recorded by MarkRead; zero when none.
314 LastReadID domain.ObjectID
315 // LastReadLost tells that since-last-read was asked for, a head was
316 // recorded, but that commit is gone: the diff is against the target.
317 LastReadLost bool
318}
319
320// GetMergeRequestOptions say which diff MergeRequestService.Get reads.
321type GetMergeRequestOptions struct {
322 // SinceLastRead compares the head recorded by MarkRead with the head (see Get).
323 SinceLastRead bool
324 // Files, when set, lists every changed file, without the file-count
325 // limit, and reads the hunks of the files in the window only, each within
326 // the per-file and the total limits of domain.DefaultDiffLimits; generated
327 // files are listed collapsed (domain.OmitGenerated). With Full, the single
328 // file is read without the line limits. Unset, the diff is bounded by
329 // domain.DefaultDiffLimits and generated files are read (the API contract).
330 Files *FileWindow
331 // Base and Head, when not zero, are the diff the caller shows: Get
332 // returns domain.ErrConflict when the request's diff base or head differ
333 // (the branch moved, a read was marked).
334 Base, Head domain.ObjectID
335 // DiffOnly skips the commits of the request (Commits stays empty).
336 DiffOnly bool
337 // Load, when set with Files, is the index of a collapsed file Get reads in
338 // full and splices into the diff in place of its collapsed entry (the page
339 // of a client without scripts). Ignored when the index is out of range,
340 // the file is not collapsed, or there is no diff.
341 Load *int
342}
343
344// FileWindow is the range [From, To) of files whose hunks are read.
345//
346// Full asks for the one file From read in full: no line limit applies, only
347// the byte guard (domain.OneFileDiffLimits). It requires To == From+1; a
348// window with Full and any other To is domain.ErrInvalid.
349type FileWindow struct {
350 From, To int
351 Full bool
352}
353
354// GetCommitOptions say which diff BrowseService.GetCommit reads.
355type GetCommitOptions struct {
356 // Files, when set, reads the hunks of a window of the files, as
357 // GetMergeRequestOptions.Files does. Unset (the zero value), the diff is
358 // bounded by domain.DefaultDiffLimits.
359 Files *FileWindow
360 // Page, without Files, reads the diff for a page: every file listed, the
361 // hunks of the first domain.CommitPageFiles files read, generated files
362 // collapsed, and the files past the window collapsed too
363 // (domain.OmitBudget). Combined with Files it is domain.ErrInvalid.
364 Page bool
365 // Load is the index of a collapsed file read in full and spliced into the
366 // diff, as GetMergeRequestOptions.Load. It requires Page.
367 Load *int
368}
369
370// AddCommentParams describes the comment to add.
371type AddCommentParams struct {
372 // ReplyTo names a non-deleted root; replies cannot specify an anchor or verdict.
373 ReplyTo int64
374 // StartLine starts an inclusive range, requiring a path and 1 <= StartLine < Line.
375 StartLine int
376 // Verdict requires mr:review and is only permitted on roots.
377 Verdict domain.Verdict
378 // Path, Line and Side designate a line of the diff; all empty for a
379 // general comment.
380 Path string
381 Line int
382 Side domain.DiffSide
383 Text string
384}
385
386// MergeParams describes the merge to perform.
387type MergeParams struct {
388 // IgnoreChecks bypasses checks and requires mr:force-merge.
389 IgnoreChecks bool
390 // Mode defaults to fast-forward.
391 Mode domain.MergeMode
392 // HeadID is the commit the caller reviewed; the source branch must still
393 // point to it.
394 HeadID domain.ObjectID
395 // DeleteSource deletes the source branch after the merge.
396 DeleteSource bool
397}
398
399// MergeResult is a merged request and what happened to its source branch.
400type MergeResult struct {
401 domain.MergeRequest
402 // SourceDeleted is true when DeleteSource was asked: the deletion is
403 // requested, and done asynchronously after the merge returned (it is
404 // skipped if the branch moved meanwhile).
405 SourceDeleted bool
406}
407
408// LoginLink is a login link as the owner receives it.
409type LoginLink struct {
410 // URL holds the secret: show it once, never log it.
411 URL string
412 ExpiresAt time.Time
413}
414
415// InboxCounts are the numbers of open items across every repository, whatever
416// the filter.
417type InboxCounts struct{ MergeRequests, Issues, Pending, Running int }
418
419// InboxOverview is what the owner has to look at across every repository: the
420// open merge requests and issues, ongoing CI runs, and issues awaiting moderation.
421type InboxOverview struct {
422 Counts InboxCounts
423 // Runs are queued or in progress, newest first, respecting the filter.
424 Runs []domain.CIRun
425 // RunsEnabled distinguishes disabled CI from an empty queue.
426 RunsEnabled bool
427 // MergeRequests and Issues are those of the filtered repository, or of
428 // every repository when there is no filter. Counts always counts all.
429 MergeRequests []domain.MergeRequest
430 Issues []domain.Issue
431 // Pending are the issues awaiting moderation, in every repository
432 // whatever the filter, oldest first. At most domain.MaxPendingIssues.
433 Pending []domain.PendingIssue
434 // Repos are the repositories that have at least one open item, by name,
435 // whatever the filter: they feed the filter itself.
436 Repos []domain.RepoName
437}
438
439// History query limits.
440const (
441 // MaxMergeRequestCommits bounds the commits returned with a merge request.
442 MaxMergeRequestCommits = 100
443 // MaxCompareCommits bounds a repository comparison.
444 MaxCompareCommits = 500
445 // MaxPushSummaryCommits bounds the commits counted in a push summary.
446 MaxPushSummaryCommits = 100
447)
448
449// ReferenceEvent says who and when a reference was recorded.
450type ReferenceEvent struct {
451 Actor string
452 At time.Time
453}
454
455// IssueEventDetail includes the current MR title, empty if the MR is gone.
456type IssueEventDetail struct {
457 Event domain.IssueEvent
458 MergeRequestTitle domain.MRTitle
459}
460
461// MergeRequestIssue is a current issue link on a merge request.
462type MergeRequestIssue struct {
463 Number int64
464 Title domain.IssueTitle
465 State domain.IssueState
466 Closes bool
467}