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