1// SPDX-License-Identifier: MIT
2
3syntax = "proto3";
4
5package ermite;
6
7import "ermite/merge_request.proto";
8import "google/protobuf/timestamp.proto";
9
10// IssueState is where an issue is in its life.
11enum IssueState {
12 // Not set. In ListIssuesRequest.state it means every state. Never returned
13 // by the server.
14 ISSUE_STATE_UNSPECIFIED = 0;
15 // The issue is open.
16 ISSUE_STATE_OPEN = 1;
17 // The issue is closed without being resolved (won't fix, duplicate, ...).
18 ISSUE_STATE_CLOSED = 2;
19 // Reserved for later use: awaiting moderation, owner-only.
20 ISSUE_STATE_PENDING = 3;
21 // The issue was fixed: by the merge of a merge request with a closing
22 // reference, or by hand (ResolveIssue). CLOSED means closed without being
23 // resolved.
24 ISSUE_STATE_RESOLVED = 4;
25}
26
27// Issue is a discussion in a repository.
28message Issue {
29 // Repo is the name of the repository.
30 string repo = 1;
31 // Number identifies the issue within its repository.
32 int64 number = 2;
33 // Title is the one-line title.
34 string title = 3;
35 // Body is the content of the issue. It may be empty.
36 string body = 4;
37 // State is where the issue is in its life.
38 IssueState state = 5;
39 // Author names who created the issue: the label of the token or session
40 // that was used ("root" for the root token, "owner" for a web session), or,
41 // for an issue approved from the public form, the unverified display name
42 // the visitor typed (or "anonymous"). It is informational and never proves
43 // identity: labels are not unique, the token may be gone, and a visitor can
44 // type "owner".
45 string author = 6;
46 // CreatedAt is when the issue was created.
47 google.protobuf.Timestamp created_at = 7;
48 // UpdatedAt is when the issue last changed.
49 google.protobuf.Timestamp updated_at = 8;
50 // CommentCount is the number of comments on the issue.
51 int32 comment_count = 9;
52 // ClosedByMergeRequest is the number of the merge request whose merge
53 // resolved the issue (a closing reference such as "Fixes #N"); 0 when the
54 // issue was resolved by hand, is closed, or is open. Reset when the issue is
55 // reopened.
56 int64 closed_by_merge_request = 10;
57}
58
59// IssueReference is a merge request of the same repository that names the
60// issue ("#N") in its title, description, notes or commit messages.
61message IssueReference {
62 // Number is the number of the merge request.
63 int64 number = 1;
64 // Title is the title of the merge request.
65 string title = 2;
66 // State is where the merge request is in its life.
67 MergeRequestState state = 3;
68 // Closes says that a closing reference ("Fixes #N") names the issue:
69 // merging the request closes it.
70 bool closes = 4;
71}
72
73// IssueComment is a note on an issue.
74message IssueComment {
75 // Id identifies the comment within its issue. Opaque.
76 string id = 1;
77 // Author names who wrote the comment, like Issue.author.
78 string author = 2;
79 // Text is the content of the comment.
80 string text = 3;
81 // CreatedAt is when the comment was written.
82 google.protobuf.Timestamp created_at = 4;
83}
84
85// IssueService manages issues and their comments.
86//
87// Servers that implement this service list "issues" in
88// GetVersionResponse.features. Issues and merge requests of a repository share
89// one number sequence. When the issues mode of the repository is off, every
90// call answers not_found, as for a repository that does not exist.
91//
92// Reading (ListIssues, GetIssue) needs the same access as reading the
93// repository: nothing for a public one, the repo:read scope for a private one.
94// Writing (CreateIssue, AddIssueComment, CloseIssue, ResolveIssue,
95// ReopenIssue) needs the
96// owner or a token with the issue:write scope on the repository; branch
97// prefixes do not restrict it. A caller without credentials gets
98// unauthenticated, one with an insufficient scope gets permission_denied, and
99// one that cannot read the repository gets not_found.
100//
101// A closing reference ("Fixes #N", "Closes #N", "Resolves #N") in the title,
102// description or commit messages of a merge request resolves the issue when the
103// request is merged, and only then: the issue gets the state RESOLVED, an
104// issue.resolved event is sent, and Issue carries closed_by_merge_request.
105// Servers that report these links (GetIssueResponse.references,
106// Issue.closed_by_merge_request) list "issues.links" in
107// GetVersionResponse.features; older servers leave them empty. Servers that
108// also keep the history of issues (GetIssueResponse.events), implement
109// ResolveIssue and the RESOLVED state list "issues.resolve".
110//
111// Text fields are normalized to NFC and stripped of control and Unicode
112// bidirectional characters (line breaks and tabs stay in bodies and comments).
113// The title is one line of 1 to 120 code points; body and comment text are at
114// most 10,000 code points and a comment must not be blank. A text that breaks
115// a rule is rejected with invalid_argument. A comment on an issue that already
116// holds 1,000 comments gives resource_exhausted. Closing or resolving an issue
117// that is not open, or reopening one that is neither closed nor resolved,
118// gives failed_precondition.
119//
120// The ordinary RPCs return issues in state OPEN, CLOSED or RESOLVED, and
121// ListIssues accepts these three states; ISSUE_STATE_PENDING is
122// invalid_argument. Submissions
123// from the public form wait as PendingIssue records, reachable only through
124// the three moderation RPCs (ListPendingIssues, ApproveIssue, RejectIssue).
125// These need owner rights (root, an owner session, or a token holding every
126// scope with no repository or branch limit); anyone else gets not_found,
127// whatever the request holds. For the owner, a malformed pending issue id is
128// not_found and a malformed repository name is invalid_argument.
129service IssueService {
130 // CreateIssue opens an issue. Needs the issue:write scope.
131 rpc CreateIssue(CreateIssueRequest) returns (CreateIssueResponse);
132 // ListIssues lists issues, highest number first, optionally in one state.
133 rpc ListIssues(ListIssuesRequest) returns (ListIssuesResponse);
134 // GetIssue returns an issue with its comments.
135 rpc GetIssue(GetIssueRequest) returns (GetIssueResponse);
136 // AddIssueComment adds a comment. Needs the issue:write scope.
137 rpc AddIssueComment(AddIssueCommentRequest) returns (AddIssueCommentResponse);
138 // CloseIssue closes an issue. Needs the issue:write scope.
139 rpc CloseIssue(CloseIssueRequest) returns (CloseIssueResponse);
140 // ResolveIssue marks an open issue as resolved. Needs the issue:write scope.
141 rpc ResolveIssue(ResolveIssueRequest) returns (ResolveIssueResponse);
142 // ReopenIssue reopens a closed or resolved issue. Needs the issue:write
143 // scope.
144 rpc ReopenIssue(ReopenIssueRequest) returns (ReopenIssueResponse);
145 // ListPendingIssues lists issues awaiting moderation, oldest first. Owner
146 // rights only; anyone else gets NOT_FOUND.
147 rpc ListPendingIssues(ListPendingIssuesRequest) returns (ListPendingIssuesResponse);
148 // ApproveIssue turns a pending issue into an open issue with a new number.
149 // Owner rights only; anyone else gets NOT_FOUND.
150 rpc ApproveIssue(ApproveIssueRequest) returns (ApproveIssueResponse);
151 // RejectIssue discards a pending issue, optionally banning its submitter.
152 // Owner rights only; anyone else gets NOT_FOUND.
153 rpc RejectIssue(RejectIssueRequest) returns (RejectIssueResponse);
154}
155
156// CreateIssueRequest describes the issue to open.
157message CreateIssueRequest {
158 // Repo is the repository name.
159 string repo = 1;
160 // Title is the one-line title. Required.
161 string title = 2;
162 // Body is the optional content.
163 string body = 3;
164}
165
166// CreateIssueResponse holds the new issue.
167message CreateIssueResponse {
168 // Issue is the created issue.
169 Issue issue = 1;
170}
171
172// ListIssuesRequest selects a page of issues.
173message ListIssuesRequest {
174 // Repo is the repository name.
175 string repo = 1;
176 // State restricts the result to one state. ISSUE_STATE_UNSPECIFIED means
177 // every state.
178 IssueState state = 2;
179 // PageSize is the maximum number of issues to return. 0 means the server
180 // default; values above the server maximum are lowered to it.
181 int32 page_size = 3;
182 // PageToken is the next_page_token of a previous response, to continue the
183 // same listing. Empty for the first page. Other fields must be unchanged.
184 string page_token = 4;
185}
186
187// ListIssuesResponse is one page of issues.
188message ListIssuesResponse {
189 // Issues are the issues of this page, highest number first.
190 repeated Issue issues = 1;
191 // NextPageToken is passed as page_token to get the next page. Empty when
192 // there is no next page.
193 string next_page_token = 2;
194}
195
196// GetIssueRequest selects an issue.
197message GetIssueRequest {
198 // Repo is the repository name.
199 string repo = 1;
200 // Number is the issue number.
201 int64 number = 2;
202}
203
204// GetIssueResponse holds an issue with its comments.
205message GetIssueResponse {
206 // Issue is the issue.
207 Issue issue = 1;
208 // Comments are every comment of the issue, oldest first.
209 repeated IssueComment comments = 2;
210 // References are the merge requests naming the issue, ascending by number,
211 // at most 100.
212 repeated IssueReference references = 3;
213 // Events are the history of the issue, oldest first, at most 500 (a server
214 // limit). Clients interleave them with the comments by created_at.
215 repeated IssueEvent events = 4;
216}
217
218// AddIssueCommentRequest describes the comment to add.
219message AddIssueCommentRequest {
220 // Repo is the repository name.
221 string repo = 1;
222 // Number is the issue number.
223 int64 number = 2;
224 // Text is the content. Required.
225 string text = 3;
226}
227
228// AddIssueCommentResponse holds the new comment.
229message AddIssueCommentResponse {
230 // Comment is the created comment.
231 IssueComment comment = 1;
232}
233
234// CloseIssueRequest names the issue to close.
235message CloseIssueRequest {
236 // Repo is the repository name.
237 string repo = 1;
238 // Number is the issue number.
239 int64 number = 2;
240}
241
242// CloseIssueResponse holds the issue after closing.
243message CloseIssueResponse {
244 // Issue is the closed issue.
245 Issue issue = 1;
246}
247
248// ResolveIssueRequest names the issue to resolve.
249message ResolveIssueRequest {
250 // Repo is the repository name.
251 string repo = 1;
252 // Number is the issue number.
253 int64 number = 2;
254}
255
256// ResolveIssueResponse holds the issue after resolving.
257message ResolveIssueResponse {
258 // Issue is the resolved issue.
259 Issue issue = 1;
260}
261
262// ReopenIssueRequest names the issue to reopen.
263message ReopenIssueRequest {
264 // Repo is the repository name.
265 string repo = 1;
266 // Number is the issue number.
267 int64 number = 2;
268}
269
270// ReopenIssueResponse holds the issue after reopening.
271message ReopenIssueResponse {
272 // Issue is the reopened issue.
273 Issue issue = 1;
274}
275
276// PendingIssue is an issue awaiting moderation. It has no number yet.
277message PendingIssue {
278 // Id identifies the pending issue. Opaque internal id, not a number.
279 string id = 1;
280 // Repo is the name of the repository.
281 string repo = 2;
282 // Title is the one-line title.
283 string title = 3;
284 // Body is the content. It may be empty.
285 string body = 4;
286 // Author is the optional display name given by the anonymous submitter. It
287 // may be empty or "anonymous", and is not verified: it never proves
288 // identity (a visitor can type "owner"). It becomes Issue.author on approval.
289 string author = 5;
290 // CreatedAt is when the issue was submitted.
291 google.protobuf.Timestamp created_at = 6;
292}
293
294// ListPendingIssuesRequest selects a page of pending issues.
295message ListPendingIssuesRequest {
296 // Repo is the repository name. Empty means every repository.
297 string repo = 1;
298 // PageSize is the maximum number of issues to return. 0 means the server
299 // default; values above the server maximum are lowered to it.
300 int32 page_size = 2;
301 // PageToken is the next_page_token of a previous response, to continue the
302 // same listing. Empty for the first page. Other fields must be unchanged.
303 string page_token = 3;
304}
305
306// ListPendingIssuesResponse is one page of pending issues.
307message ListPendingIssuesResponse {
308 // Issues are the pending issues of this page, oldest first.
309 repeated PendingIssue issues = 1;
310 // NextPageToken is passed as page_token to get the next page. Empty when
311 // there is no next page.
312 string next_page_token = 2;
313}
314
315// ApproveIssueRequest names the pending issue to approve.
316message ApproveIssueRequest {
317 // Repo is the repository name.
318 string repo = 1;
319 // Id is the PendingIssue.id.
320 string id = 2;
321}
322
323// ApproveIssueResponse holds the approved issue.
324message ApproveIssueResponse {
325 // Issue is the approved issue, with its new number and state
326 // ISSUE_STATE_OPEN.
327 Issue issue = 1;
328}
329
330// RejectIssueRequest names the pending issue to reject.
331message RejectIssueRequest {
332 // Repo is the repository name.
333 string repo = 1;
334 // Id is the PendingIssue.id.
335 string id = 2;
336 // Ban also bans the submitter for a while.
337 bool ban = 3;
338}
339
340// RejectIssueResponse is empty: the pending issue is gone.
341message RejectIssueResponse {}