detached bfc173e

feat(api): add pending issue moderation and the issue submitted event

commit
bfc173ed3efb28b65810d2ebdd925cba87887477
author
brice · · 2026-10-19 06:50 UTC
committer
brice · · 2026-10-19 08:28 UTC
parent
3130756
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
7 files · +856 -109
M gen/ermite/ermiteconnect/event.connect.go +4 -2
@@ -49,7 +49,8 @@ type EventServiceClient interface {
4949 // for merge requests, merge_request_opened, merge_request_merged,
5050 // merge_request_closed, comment_added, comment_edited, comment_deleted and
5151 // comment_resolved (a thread resolved or reopened); for issues, issue_opened,
52- // issue_closed, issue_reopened and issue_commented. Each one is delivered on the
52+ // issue_closed, issue_reopened and issue_commented (and issue_submitted, to
53+ // owner watchers only). Each one is delivered on the
5354 // "repo:<name>" topic of its repository and on "home", subject to the
5455 // visibility of the repository.
5556 //
@@ -123,7 +124,8 @@ type EventServiceHandler interface {
123124 // for merge requests, merge_request_opened, merge_request_merged,
124125 // merge_request_closed, comment_added, comment_edited, comment_deleted and
125126 // comment_resolved (a thread resolved or reopened); for issues, issue_opened,
126- // issue_closed, issue_reopened and issue_commented. Each one is delivered on the
127+ // issue_closed, issue_reopened and issue_commented (and issue_submitted, to
128+ // owner watchers only). Each one is delivered on the
127129 // "repo:<name>" topic of its repository and on "home", subject to the
128130 // visibility of the repository.
129131 //
@@ -49,7 +49,8 @@ type EventServiceClient interface {
49 // for merge requests, merge_request_opened, merge_request_merged,49 // for merge requests, merge_request_opened, merge_request_merged,
50 // merge_request_closed, comment_added, comment_edited, comment_deleted and50 // merge_request_closed, comment_added, comment_edited, comment_deleted and
51 // comment_resolved (a thread resolved or reopened); for issues, issue_opened,51 // comment_resolved (a thread resolved or reopened); for issues, issue_opened,
52- // issue_closed, issue_reopened and issue_commented. Each one is delivered on the52+ // issue_closed, issue_reopened and issue_commented (and issue_submitted, to
53+ // owner watchers only). Each one is delivered on the
53 // "repo:<name>" topic of its repository and on "home", subject to the54 // "repo:<name>" topic of its repository and on "home", subject to the
54 // visibility of the repository.55 // visibility of the repository.
55 //56 //
@@ -123,7 +124,8 @@ type EventServiceHandler interface {
123 // for merge requests, merge_request_opened, merge_request_merged,124 // for merge requests, merge_request_opened, merge_request_merged,
124 // merge_request_closed, comment_added, comment_edited, comment_deleted and125 // merge_request_closed, comment_added, comment_edited, comment_deleted and
125 // comment_resolved (a thread resolved or reopened); for issues, issue_opened,126 // comment_resolved (a thread resolved or reopened); for issues, issue_opened,
126- // issue_closed, issue_reopened and issue_commented. Each one is delivered on the127+ // issue_closed, issue_reopened and issue_commented (and issue_submitted, to
128+ // owner watchers only). Each one is delivered on the
127 // "repo:<name>" topic of its repository and on "home", subject to the129 // "repo:<name>" topic of its repository and on "home", subject to the
128 // visibility of the repository.130 // visibility of the repository.
129 //131 //
M gen/ermite/ermiteconnect/issue.connect.go +105 -6
@@ -50,6 +50,15 @@ const (
5050 // IssueServiceReopenIssueProcedure is the fully-qualified name of the IssueService's ReopenIssue
5151 // RPC.
5252 IssueServiceReopenIssueProcedure = "/ermite.IssueService/ReopenIssue"
53+ // IssueServiceListPendingIssuesProcedure is the fully-qualified name of the IssueService's
54+ // ListPendingIssues RPC.
55+ IssueServiceListPendingIssuesProcedure = "/ermite.IssueService/ListPendingIssues"
56+ // IssueServiceApproveIssueProcedure is the fully-qualified name of the IssueService's ApproveIssue
57+ // RPC.
58+ IssueServiceApproveIssueProcedure = "/ermite.IssueService/ApproveIssue"
59+ // IssueServiceRejectIssueProcedure is the fully-qualified name of the IssueService's RejectIssue
60+ // RPC.
61+ IssueServiceRejectIssueProcedure = "/ermite.IssueService/RejectIssue"
5362 )
5463
5564 // IssueServiceClient is a client for the ermite.IssueService service.
@@ -66,6 +75,15 @@ type IssueServiceClient interface {
6675 CloseIssue(context.Context, *connect.Request[ermite.CloseIssueRequest]) (*connect.Response[ermite.CloseIssueResponse], error)
6776 // ReopenIssue reopens an issue. Needs the issue:write scope.
6877 ReopenIssue(context.Context, *connect.Request[ermite.ReopenIssueRequest]) (*connect.Response[ermite.ReopenIssueResponse], error)
78+ // ListPendingIssues lists issues awaiting moderation, oldest first. Owner
79+ // rights only; anyone else gets NOT_FOUND.
80+ ListPendingIssues(context.Context, *connect.Request[ermite.ListPendingIssuesRequest]) (*connect.Response[ermite.ListPendingIssuesResponse], error)
81+ // ApproveIssue turns a pending issue into an open issue with a new number.
82+ // Owner rights only; anyone else gets NOT_FOUND.
83+ ApproveIssue(context.Context, *connect.Request[ermite.ApproveIssueRequest]) (*connect.Response[ermite.ApproveIssueResponse], error)
84+ // RejectIssue discards a pending issue, optionally banning its submitter.
85+ // Owner rights only; anyone else gets NOT_FOUND.
86+ RejectIssue(context.Context, *connect.Request[ermite.RejectIssueRequest]) (*connect.Response[ermite.RejectIssueResponse], error)
6987 }
7088
7189 // NewIssueServiceClient constructs a client for the ermite.IssueService service. By default, it
@@ -115,17 +133,38 @@ func NewIssueServiceClient(httpClient connect.HTTPClient, baseURL string, opts .
115133 connect.WithSchema(issueServiceMethods.ByName("ReopenIssue")),
116134 connect.WithClientOptions(opts...),
117135 ),
136+ listPendingIssues: connect.NewClient[ermite.ListPendingIssuesRequest, ermite.ListPendingIssuesResponse](
137+ httpClient,
138+ baseURL+IssueServiceListPendingIssuesProcedure,
139+ connect.WithSchema(issueServiceMethods.ByName("ListPendingIssues")),
140+ connect.WithClientOptions(opts...),
141+ ),
142+ approveIssue: connect.NewClient[ermite.ApproveIssueRequest, ermite.ApproveIssueResponse](
143+ httpClient,
144+ baseURL+IssueServiceApproveIssueProcedure,
145+ connect.WithSchema(issueServiceMethods.ByName("ApproveIssue")),
146+ connect.WithClientOptions(opts...),
147+ ),
148+ rejectIssue: connect.NewClient[ermite.RejectIssueRequest, ermite.RejectIssueResponse](
149+ httpClient,
150+ baseURL+IssueServiceRejectIssueProcedure,
151+ connect.WithSchema(issueServiceMethods.ByName("RejectIssue")),
152+ connect.WithClientOptions(opts...),
153+ ),
118154 }
119155 }
120156
121157 // issueServiceClient implements IssueServiceClient.
122158 type issueServiceClient struct {
123- createIssue *connect.Client[ermite.CreateIssueRequest, ermite.CreateIssueResponse]
124- listIssues *connect.Client[ermite.ListIssuesRequest, ermite.ListIssuesResponse]
125- getIssue *connect.Client[ermite.GetIssueRequest, ermite.GetIssueResponse]
126- addIssueComment *connect.Client[ermite.AddIssueCommentRequest, ermite.AddIssueCommentResponse]
127- closeIssue *connect.Client[ermite.CloseIssueRequest, ermite.CloseIssueResponse]
128- reopenIssue *connect.Client[ermite.ReopenIssueRequest, ermite.ReopenIssueResponse]
159+ createIssue *connect.Client[ermite.CreateIssueRequest, ermite.CreateIssueResponse]
160+ listIssues *connect.Client[ermite.ListIssuesRequest, ermite.ListIssuesResponse]
161+ getIssue *connect.Client[ermite.GetIssueRequest, ermite.GetIssueResponse]
162+ addIssueComment *connect.Client[ermite.AddIssueCommentRequest, ermite.AddIssueCommentResponse]
163+ closeIssue *connect.Client[ermite.CloseIssueRequest, ermite.CloseIssueResponse]
164+ reopenIssue *connect.Client[ermite.ReopenIssueRequest, ermite.ReopenIssueResponse]
165+ listPendingIssues *connect.Client[ermite.ListPendingIssuesRequest, ermite.ListPendingIssuesResponse]
166+ approveIssue *connect.Client[ermite.ApproveIssueRequest, ermite.ApproveIssueResponse]
167+ rejectIssue *connect.Client[ermite.RejectIssueRequest, ermite.RejectIssueResponse]
129168 }
130169
131170 // CreateIssue calls ermite.IssueService.CreateIssue.
@@ -158,6 +197,21 @@ func (c *issueServiceClient) ReopenIssue(ctx context.Context, req *connect.Reque
158197 return c.reopenIssue.CallUnary(ctx, req)
159198 }
160199
200+// ListPendingIssues calls ermite.IssueService.ListPendingIssues.
201+func (c *issueServiceClient) ListPendingIssues(ctx context.Context, req *connect.Request[ermite.ListPendingIssuesRequest]) (*connect.Response[ermite.ListPendingIssuesResponse], error) {
202+ return c.listPendingIssues.CallUnary(ctx, req)
203+}
204+
205+// ApproveIssue calls ermite.IssueService.ApproveIssue.
206+func (c *issueServiceClient) ApproveIssue(ctx context.Context, req *connect.Request[ermite.ApproveIssueRequest]) (*connect.Response[ermite.ApproveIssueResponse], error) {
207+ return c.approveIssue.CallUnary(ctx, req)
208+}
209+
210+// RejectIssue calls ermite.IssueService.RejectIssue.
211+func (c *issueServiceClient) RejectIssue(ctx context.Context, req *connect.Request[ermite.RejectIssueRequest]) (*connect.Response[ermite.RejectIssueResponse], error) {
212+ return c.rejectIssue.CallUnary(ctx, req)
213+}
214+
161215 // IssueServiceHandler is an implementation of the ermite.IssueService service.
162216 type IssueServiceHandler interface {
163217 // CreateIssue opens an issue. Needs the issue:write scope.
@@ -172,6 +226,15 @@ type IssueServiceHandler interface {
172226 CloseIssue(context.Context, *connect.Request[ermite.CloseIssueRequest]) (*connect.Response[ermite.CloseIssueResponse], error)
173227 // ReopenIssue reopens an issue. Needs the issue:write scope.
174228 ReopenIssue(context.Context, *connect.Request[ermite.ReopenIssueRequest]) (*connect.Response[ermite.ReopenIssueResponse], error)
229+ // ListPendingIssues lists issues awaiting moderation, oldest first. Owner
230+ // rights only; anyone else gets NOT_FOUND.
231+ ListPendingIssues(context.Context, *connect.Request[ermite.ListPendingIssuesRequest]) (*connect.Response[ermite.ListPendingIssuesResponse], error)
232+ // ApproveIssue turns a pending issue into an open issue with a new number.
233+ // Owner rights only; anyone else gets NOT_FOUND.
234+ ApproveIssue(context.Context, *connect.Request[ermite.ApproveIssueRequest]) (*connect.Response[ermite.ApproveIssueResponse], error)
235+ // RejectIssue discards a pending issue, optionally banning its submitter.
236+ // Owner rights only; anyone else gets NOT_FOUND.
237+ RejectIssue(context.Context, *connect.Request[ermite.RejectIssueRequest]) (*connect.Response[ermite.RejectIssueResponse], error)
175238 }
176239
177240 // NewIssueServiceHandler builds an HTTP handler from the service implementation. It returns the
@@ -217,6 +280,24 @@ func NewIssueServiceHandler(svc IssueServiceHandler, opts ...connect.HandlerOpti
217280 connect.WithSchema(issueServiceMethods.ByName("ReopenIssue")),
218281 connect.WithHandlerOptions(opts...),
219282 )
283+ issueServiceListPendingIssuesHandler := connect.NewUnaryHandler(
284+ IssueServiceListPendingIssuesProcedure,
285+ svc.ListPendingIssues,
286+ connect.WithSchema(issueServiceMethods.ByName("ListPendingIssues")),
287+ connect.WithHandlerOptions(opts...),
288+ )
289+ issueServiceApproveIssueHandler := connect.NewUnaryHandler(
290+ IssueServiceApproveIssueProcedure,
291+ svc.ApproveIssue,
292+ connect.WithSchema(issueServiceMethods.ByName("ApproveIssue")),
293+ connect.WithHandlerOptions(opts...),
294+ )
295+ issueServiceRejectIssueHandler := connect.NewUnaryHandler(
296+ IssueServiceRejectIssueProcedure,
297+ svc.RejectIssue,
298+ connect.WithSchema(issueServiceMethods.ByName("RejectIssue")),
299+ connect.WithHandlerOptions(opts...),
300+ )
220301 return "/ermite.IssueService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
221302 switch r.URL.Path {
222303 case IssueServiceCreateIssueProcedure:
@@ -231,6 +312,12 @@ func NewIssueServiceHandler(svc IssueServiceHandler, opts ...connect.HandlerOpti
231312 issueServiceCloseIssueHandler.ServeHTTP(w, r)
232313 case IssueServiceReopenIssueProcedure:
233314 issueServiceReopenIssueHandler.ServeHTTP(w, r)
315+ case IssueServiceListPendingIssuesProcedure:
316+ issueServiceListPendingIssuesHandler.ServeHTTP(w, r)
317+ case IssueServiceApproveIssueProcedure:
318+ issueServiceApproveIssueHandler.ServeHTTP(w, r)
319+ case IssueServiceRejectIssueProcedure:
320+ issueServiceRejectIssueHandler.ServeHTTP(w, r)
234321 default:
235322 http.NotFound(w, r)
236323 }
@@ -263,3 +350,15 @@ func (UnimplementedIssueServiceHandler) CloseIssue(context.Context, *connect.Req
263350 func (UnimplementedIssueServiceHandler) ReopenIssue(context.Context, *connect.Request[ermite.ReopenIssueRequest]) (*connect.Response[ermite.ReopenIssueResponse], error) {
264351 return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.IssueService.ReopenIssue is not implemented"))
265352 }
353+
354+func (UnimplementedIssueServiceHandler) ListPendingIssues(context.Context, *connect.Request[ermite.ListPendingIssuesRequest]) (*connect.Response[ermite.ListPendingIssuesResponse], error) {
355+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.IssueService.ListPendingIssues is not implemented"))
356+}
357+
358+func (UnimplementedIssueServiceHandler) ApproveIssue(context.Context, *connect.Request[ermite.ApproveIssueRequest]) (*connect.Response[ermite.ApproveIssueResponse], error) {
359+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.IssueService.ApproveIssue is not implemented"))
360+}
361+
362+func (UnimplementedIssueServiceHandler) RejectIssue(context.Context, *connect.Request[ermite.RejectIssueRequest]) (*connect.Response[ermite.RejectIssueResponse], error) {
363+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.IssueService.RejectIssue is not implemented"))
364+}
@@ -50,6 +50,15 @@ const (
50 // IssueServiceReopenIssueProcedure is the fully-qualified name of the IssueService's ReopenIssue50 // IssueServiceReopenIssueProcedure is the fully-qualified name of the IssueService's ReopenIssue
51 // RPC.51 // RPC.
52 IssueServiceReopenIssueProcedure = "/ermite.IssueService/ReopenIssue"52 IssueServiceReopenIssueProcedure = "/ermite.IssueService/ReopenIssue"
53+ // IssueServiceListPendingIssuesProcedure is the fully-qualified name of the IssueService's
54+ // ListPendingIssues RPC.
55+ IssueServiceListPendingIssuesProcedure = "/ermite.IssueService/ListPendingIssues"
56+ // IssueServiceApproveIssueProcedure is the fully-qualified name of the IssueService's ApproveIssue
57+ // RPC.
58+ IssueServiceApproveIssueProcedure = "/ermite.IssueService/ApproveIssue"
59+ // IssueServiceRejectIssueProcedure is the fully-qualified name of the IssueService's RejectIssue
60+ // RPC.
61+ IssueServiceRejectIssueProcedure = "/ermite.IssueService/RejectIssue"
53 )62 )
54 63
55 // IssueServiceClient is a client for the ermite.IssueService service.64 // IssueServiceClient is a client for the ermite.IssueService service.
@@ -66,6 +75,15 @@ type IssueServiceClient interface {
66 CloseIssue(context.Context, *connect.Request[ermite.CloseIssueRequest]) (*connect.Response[ermite.CloseIssueResponse], error)75 CloseIssue(context.Context, *connect.Request[ermite.CloseIssueRequest]) (*connect.Response[ermite.CloseIssueResponse], error)
67 // ReopenIssue reopens an issue. Needs the issue:write scope.76 // ReopenIssue reopens an issue. Needs the issue:write scope.
68 ReopenIssue(context.Context, *connect.Request[ermite.ReopenIssueRequest]) (*connect.Response[ermite.ReopenIssueResponse], error)77 ReopenIssue(context.Context, *connect.Request[ermite.ReopenIssueRequest]) (*connect.Response[ermite.ReopenIssueResponse], error)
78+ // ListPendingIssues lists issues awaiting moderation, oldest first. Owner
79+ // rights only; anyone else gets NOT_FOUND.
80+ ListPendingIssues(context.Context, *connect.Request[ermite.ListPendingIssuesRequest]) (*connect.Response[ermite.ListPendingIssuesResponse], error)
81+ // ApproveIssue turns a pending issue into an open issue with a new number.
82+ // Owner rights only; anyone else gets NOT_FOUND.
83+ ApproveIssue(context.Context, *connect.Request[ermite.ApproveIssueRequest]) (*connect.Response[ermite.ApproveIssueResponse], error)
84+ // RejectIssue discards a pending issue, optionally banning its submitter.
85+ // Owner rights only; anyone else gets NOT_FOUND.
86+ RejectIssue(context.Context, *connect.Request[ermite.RejectIssueRequest]) (*connect.Response[ermite.RejectIssueResponse], error)
69 }87 }
70 88
71 // NewIssueServiceClient constructs a client for the ermite.IssueService service. By default, it89 // NewIssueServiceClient constructs a client for the ermite.IssueService service. By default, it
@@ -115,17 +133,38 @@ func NewIssueServiceClient(httpClient connect.HTTPClient, baseURL string, opts .
115 connect.WithSchema(issueServiceMethods.ByName("ReopenIssue")),133 connect.WithSchema(issueServiceMethods.ByName("ReopenIssue")),
116 connect.WithClientOptions(opts...),134 connect.WithClientOptions(opts...),
117 ),135 ),
136+ listPendingIssues: connect.NewClient[ermite.ListPendingIssuesRequest, ermite.ListPendingIssuesResponse](
137+ httpClient,
138+ baseURL+IssueServiceListPendingIssuesProcedure,
139+ connect.WithSchema(issueServiceMethods.ByName("ListPendingIssues")),
140+ connect.WithClientOptions(opts...),
141+ ),
142+ approveIssue: connect.NewClient[ermite.ApproveIssueRequest, ermite.ApproveIssueResponse](
143+ httpClient,
144+ baseURL+IssueServiceApproveIssueProcedure,
145+ connect.WithSchema(issueServiceMethods.ByName("ApproveIssue")),
146+ connect.WithClientOptions(opts...),
147+ ),
148+ rejectIssue: connect.NewClient[ermite.RejectIssueRequest, ermite.RejectIssueResponse](
149+ httpClient,
150+ baseURL+IssueServiceRejectIssueProcedure,
151+ connect.WithSchema(issueServiceMethods.ByName("RejectIssue")),
152+ connect.WithClientOptions(opts...),
153+ ),
118 }154 }
119 }155 }
120 156
121 // issueServiceClient implements IssueServiceClient.157 // issueServiceClient implements IssueServiceClient.
122 type issueServiceClient struct {158 type issueServiceClient struct {
123- createIssue *connect.Client[ermite.CreateIssueRequest, ermite.CreateIssueResponse]159+ createIssue *connect.Client[ermite.CreateIssueRequest, ermite.CreateIssueResponse]
124- listIssues *connect.Client[ermite.ListIssuesRequest, ermite.ListIssuesResponse]160+ listIssues *connect.Client[ermite.ListIssuesRequest, ermite.ListIssuesResponse]
125- getIssue *connect.Client[ermite.GetIssueRequest, ermite.GetIssueResponse]161+ getIssue *connect.Client[ermite.GetIssueRequest, ermite.GetIssueResponse]
126- addIssueComment *connect.Client[ermite.AddIssueCommentRequest, ermite.AddIssueCommentResponse]162+ addIssueComment *connect.Client[ermite.AddIssueCommentRequest, ermite.AddIssueCommentResponse]
127- closeIssue *connect.Client[ermite.CloseIssueRequest, ermite.CloseIssueResponse]163+ closeIssue *connect.Client[ermite.CloseIssueRequest, ermite.CloseIssueResponse]
128- reopenIssue *connect.Client[ermite.ReopenIssueRequest, ermite.ReopenIssueResponse]164+ reopenIssue *connect.Client[ermite.ReopenIssueRequest, ermite.ReopenIssueResponse]
165+ listPendingIssues *connect.Client[ermite.ListPendingIssuesRequest, ermite.ListPendingIssuesResponse]
166+ approveIssue *connect.Client[ermite.ApproveIssueRequest, ermite.ApproveIssueResponse]
167+ rejectIssue *connect.Client[ermite.RejectIssueRequest, ermite.RejectIssueResponse]
129 }168 }
130 169
131 // CreateIssue calls ermite.IssueService.CreateIssue.170 // CreateIssue calls ermite.IssueService.CreateIssue.
@@ -158,6 +197,21 @@ func (c *issueServiceClient) ReopenIssue(ctx context.Context, req *connect.Reque
158 return c.reopenIssue.CallUnary(ctx, req)197 return c.reopenIssue.CallUnary(ctx, req)
159 }198 }
160 199
200+// ListPendingIssues calls ermite.IssueService.ListPendingIssues.
201+func (c *issueServiceClient) ListPendingIssues(ctx context.Context, req *connect.Request[ermite.ListPendingIssuesRequest]) (*connect.Response[ermite.ListPendingIssuesResponse], error) {
202+ return c.listPendingIssues.CallUnary(ctx, req)
203+}
204+
205+// ApproveIssue calls ermite.IssueService.ApproveIssue.
206+func (c *issueServiceClient) ApproveIssue(ctx context.Context, req *connect.Request[ermite.ApproveIssueRequest]) (*connect.Response[ermite.ApproveIssueResponse], error) {
207+ return c.approveIssue.CallUnary(ctx, req)
208+}
209+
210+// RejectIssue calls ermite.IssueService.RejectIssue.
211+func (c *issueServiceClient) RejectIssue(ctx context.Context, req *connect.Request[ermite.RejectIssueRequest]) (*connect.Response[ermite.RejectIssueResponse], error) {
212+ return c.rejectIssue.CallUnary(ctx, req)
213+}
214+
161 // IssueServiceHandler is an implementation of the ermite.IssueService service.215 // IssueServiceHandler is an implementation of the ermite.IssueService service.
162 type IssueServiceHandler interface {216 type IssueServiceHandler interface {
163 // CreateIssue opens an issue. Needs the issue:write scope.217 // CreateIssue opens an issue. Needs the issue:write scope.
@@ -172,6 +226,15 @@ type IssueServiceHandler interface {
172 CloseIssue(context.Context, *connect.Request[ermite.CloseIssueRequest]) (*connect.Response[ermite.CloseIssueResponse], error)226 CloseIssue(context.Context, *connect.Request[ermite.CloseIssueRequest]) (*connect.Response[ermite.CloseIssueResponse], error)
173 // ReopenIssue reopens an issue. Needs the issue:write scope.227 // ReopenIssue reopens an issue. Needs the issue:write scope.
174 ReopenIssue(context.Context, *connect.Request[ermite.ReopenIssueRequest]) (*connect.Response[ermite.ReopenIssueResponse], error)228 ReopenIssue(context.Context, *connect.Request[ermite.ReopenIssueRequest]) (*connect.Response[ermite.ReopenIssueResponse], error)
229+ // ListPendingIssues lists issues awaiting moderation, oldest first. Owner
230+ // rights only; anyone else gets NOT_FOUND.
231+ ListPendingIssues(context.Context, *connect.Request[ermite.ListPendingIssuesRequest]) (*connect.Response[ermite.ListPendingIssuesResponse], error)
232+ // ApproveIssue turns a pending issue into an open issue with a new number.
233+ // Owner rights only; anyone else gets NOT_FOUND.
234+ ApproveIssue(context.Context, *connect.Request[ermite.ApproveIssueRequest]) (*connect.Response[ermite.ApproveIssueResponse], error)
235+ // RejectIssue discards a pending issue, optionally banning its submitter.
236+ // Owner rights only; anyone else gets NOT_FOUND.
237+ RejectIssue(context.Context, *connect.Request[ermite.RejectIssueRequest]) (*connect.Response[ermite.RejectIssueResponse], error)
175 }238 }
176 239
177 // NewIssueServiceHandler builds an HTTP handler from the service implementation. It returns the240 // NewIssueServiceHandler builds an HTTP handler from the service implementation. It returns the
@@ -217,6 +280,24 @@ func NewIssueServiceHandler(svc IssueServiceHandler, opts ...connect.HandlerOpti
217 connect.WithSchema(issueServiceMethods.ByName("ReopenIssue")),280 connect.WithSchema(issueServiceMethods.ByName("ReopenIssue")),
218 connect.WithHandlerOptions(opts...),281 connect.WithHandlerOptions(opts...),
219 )282 )
283+ issueServiceListPendingIssuesHandler := connect.NewUnaryHandler(
284+ IssueServiceListPendingIssuesProcedure,
285+ svc.ListPendingIssues,
286+ connect.WithSchema(issueServiceMethods.ByName("ListPendingIssues")),
287+ connect.WithHandlerOptions(opts...),
288+ )
289+ issueServiceApproveIssueHandler := connect.NewUnaryHandler(
290+ IssueServiceApproveIssueProcedure,
291+ svc.ApproveIssue,
292+ connect.WithSchema(issueServiceMethods.ByName("ApproveIssue")),
293+ connect.WithHandlerOptions(opts...),
294+ )
295+ issueServiceRejectIssueHandler := connect.NewUnaryHandler(
296+ IssueServiceRejectIssueProcedure,
297+ svc.RejectIssue,
298+ connect.WithSchema(issueServiceMethods.ByName("RejectIssue")),
299+ connect.WithHandlerOptions(opts...),
300+ )
220 return "/ermite.IssueService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {301 return "/ermite.IssueService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
221 switch r.URL.Path {302 switch r.URL.Path {
222 case IssueServiceCreateIssueProcedure:303 case IssueServiceCreateIssueProcedure:
@@ -231,6 +312,12 @@ func NewIssueServiceHandler(svc IssueServiceHandler, opts ...connect.HandlerOpti
231 issueServiceCloseIssueHandler.ServeHTTP(w, r)312 issueServiceCloseIssueHandler.ServeHTTP(w, r)
232 case IssueServiceReopenIssueProcedure:313 case IssueServiceReopenIssueProcedure:
233 issueServiceReopenIssueHandler.ServeHTTP(w, r)314 issueServiceReopenIssueHandler.ServeHTTP(w, r)
315+ case IssueServiceListPendingIssuesProcedure:
316+ issueServiceListPendingIssuesHandler.ServeHTTP(w, r)
317+ case IssueServiceApproveIssueProcedure:
318+ issueServiceApproveIssueHandler.ServeHTTP(w, r)
319+ case IssueServiceRejectIssueProcedure:
320+ issueServiceRejectIssueHandler.ServeHTTP(w, r)
234 default:321 default:
235 http.NotFound(w, r)322 http.NotFound(w, r)
236 }323 }
@@ -263,3 +350,15 @@ func (UnimplementedIssueServiceHandler) CloseIssue(context.Context, *connect.Req
263 func (UnimplementedIssueServiceHandler) ReopenIssue(context.Context, *connect.Request[ermite.ReopenIssueRequest]) (*connect.Response[ermite.ReopenIssueResponse], error) {350 func (UnimplementedIssueServiceHandler) ReopenIssue(context.Context, *connect.Request[ermite.ReopenIssueRequest]) (*connect.Response[ermite.ReopenIssueResponse], error) {
264 return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.IssueService.ReopenIssue is not implemented"))351 return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.IssueService.ReopenIssue is not implemented"))
265 }352 }
353+
354+func (UnimplementedIssueServiceHandler) ListPendingIssues(context.Context, *connect.Request[ermite.ListPendingIssuesRequest]) (*connect.Response[ermite.ListPendingIssuesResponse], error) {
355+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.IssueService.ListPendingIssues is not implemented"))
356+}
357+
358+func (UnimplementedIssueServiceHandler) ApproveIssue(context.Context, *connect.Request[ermite.ApproveIssueRequest]) (*connect.Response[ermite.ApproveIssueResponse], error) {
359+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.IssueService.ApproveIssue is not implemented"))
360+}
361+
362+func (UnimplementedIssueServiceHandler) RejectIssue(context.Context, *connect.Request[ermite.RejectIssueRequest]) (*connect.Response[ermite.RejectIssueResponse], error) {
363+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.IssueService.RejectIssue is not implemented"))
364+}
M gen/ermite/event.pb.go +134 -52
@@ -161,6 +161,7 @@ type Event struct {
161161 // *Event_IssueClosed
162162 // *Event_IssueReopened
163163 // *Event_IssueCommented
164+ // *Event_IssueSubmitted
164165 Kind isEvent_Kind `protobuf_oneof:"kind"`
165166 unknownFields protoimpl.UnknownFields
166167 sizeCache protoimpl.SizeCache
@@ -379,6 +380,15 @@ func (x *Event) GetIssueCommented() *IssueCommented {
379380 return nil
380381 }
381382
383+func (x *Event) GetIssueSubmitted() *IssueSubmitted {
384+ if x != nil {
385+ if x, ok := x.Kind.(*Event_IssueSubmitted); ok {
386+ return x.IssueSubmitted
387+ }
388+ }
389+ return nil
390+}
391+
382392 type isEvent_Kind interface {
383393 isEvent_Kind()
384394 }
@@ -473,6 +483,12 @@ type Event_IssueCommented struct {
473483 IssueCommented *IssueCommented `protobuf:"bytes,20,opt,name=issue_commented,json=issueCommented,proto3,oneof"`
474484 }
475485
486+type Event_IssueSubmitted struct {
487+ // IssueSubmitted: an issue is awaiting moderation. Delivered to owner
488+ // watchers only.
489+ IssueSubmitted *IssueSubmitted `protobuf:"bytes,21,opt,name=issue_submitted,json=issueSubmitted,proto3,oneof"`
490+}
491+
476492 func (*Event_RepoCreated) isEvent_Kind() {}
477493
478494 func (*Event_RepoUpdated) isEvent_Kind() {}
@@ -509,6 +525,8 @@ func (*Event_IssueReopened) isEvent_Kind() {}
509525
510526 func (*Event_IssueCommented) isEvent_Kind() {}
511527
528+func (*Event_IssueSubmitted) isEvent_Kind() {}
529+
512530 // RepoCreated reports a new repository.
513531 type RepoCreated struct {
514532 state protoimpl.MessageState `protogen:"open.v1"`
@@ -1365,6 +1383,63 @@ func (x *IssueCommented) GetComment() *IssueComment {
13651383 return nil
13661384 }
13671385
1386+// IssueSubmitted reports an issue awaiting moderation. It is delivered to
1387+// owner watchers only. It carries no title or body: content never travels in
1388+// events, read it with IssueService.ListPendingIssues.
1389+type IssueSubmitted struct {
1390+ state protoimpl.MessageState `protogen:"open.v1"`
1391+ // Repo is the name of the repository.
1392+ Repo string `protobuf:"bytes,1,opt,name=repo,proto3" json:"repo,omitempty"`
1393+ // Id is the PendingIssue.id.
1394+ Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"`
1395+ unknownFields protoimpl.UnknownFields
1396+ sizeCache protoimpl.SizeCache
1397+}
1398+
1399+func (x *IssueSubmitted) Reset() {
1400+ *x = IssueSubmitted{}
1401+ mi := &file_ermite_event_proto_msgTypes[19]
1402+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
1403+ ms.StoreMessageInfo(mi)
1404+}
1405+
1406+func (x *IssueSubmitted) String() string {
1407+ return protoimpl.X.MessageStringOf(x)
1408+}
1409+
1410+func (*IssueSubmitted) ProtoMessage() {}
1411+
1412+func (x *IssueSubmitted) ProtoReflect() protoreflect.Message {
1413+ mi := &file_ermite_event_proto_msgTypes[19]
1414+ if x != nil {
1415+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
1416+ if ms.LoadMessageInfo() == nil {
1417+ ms.StoreMessageInfo(mi)
1418+ }
1419+ return ms
1420+ }
1421+ return mi.MessageOf(x)
1422+}
1423+
1424+// Deprecated: Use IssueSubmitted.ProtoReflect.Descriptor instead.
1425+func (*IssueSubmitted) Descriptor() ([]byte, []int) {
1426+ return file_ermite_event_proto_rawDescGZIP(), []int{19}
1427+}
1428+
1429+func (x *IssueSubmitted) GetRepo() string {
1430+ if x != nil {
1431+ return x.Repo
1432+ }
1433+ return ""
1434+}
1435+
1436+func (x *IssueSubmitted) GetId() string {
1437+ if x != nil {
1438+ return x.Id
1439+ }
1440+ return ""
1441+}
1442+
13681443 // Gap tells the client that events were lost: the requested resume point is
13691444 // older than the server's buffer. It has no fields. The client should reload
13701445 // the state it displays (repositories, refs, merge requests and their
@@ -1378,7 +1453,7 @@ type Gap struct {
13781453
13791454 func (x *Gap) Reset() {
13801455 *x = Gap{}
1381- mi := &file_ermite_event_proto_msgTypes[19]
1456+ mi := &file_ermite_event_proto_msgTypes[20]
13821457 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
13831458 ms.StoreMessageInfo(mi)
13841459 }
@@ -1390,7 +1465,7 @@ func (x *Gap) String() string {
13901465 func (*Gap) ProtoMessage() {}
13911466
13921467 func (x *Gap) ProtoReflect() protoreflect.Message {
1393- mi := &file_ermite_event_proto_msgTypes[19]
1468+ mi := &file_ermite_event_proto_msgTypes[20]
13941469 if x != nil {
13951470 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
13961471 if ms.LoadMessageInfo() == nil {
@@ -1403,7 +1478,7 @@ func (x *Gap) ProtoReflect() protoreflect.Message {
14031478
14041479 // Deprecated: Use Gap.ProtoReflect.Descriptor instead.
14051480 func (*Gap) Descriptor() ([]byte, []int) {
1406- return file_ermite_event_proto_rawDescGZIP(), []int{19}
1481+ return file_ermite_event_proto_rawDescGZIP(), []int{20}
14071482 }
14081483
14091484 // RunUpdated reports a CI run that was created or whose status, conclusion or
@@ -1418,7 +1493,7 @@ type RunUpdated struct {
14181493
14191494 func (x *RunUpdated) Reset() {
14201495 *x = RunUpdated{}
1421- mi := &file_ermite_event_proto_msgTypes[20]
1496+ mi := &file_ermite_event_proto_msgTypes[21]
14221497 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
14231498 ms.StoreMessageInfo(mi)
14241499 }
@@ -1430,7 +1505,7 @@ func (x *RunUpdated) String() string {
14301505 func (*RunUpdated) ProtoMessage() {}
14311506
14321507 func (x *RunUpdated) ProtoReflect() protoreflect.Message {
1433- mi := &file_ermite_event_proto_msgTypes[20]
1508+ mi := &file_ermite_event_proto_msgTypes[21]
14341509 if x != nil {
14351510 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
14361511 if ms.LoadMessageInfo() == nil {
@@ -1443,7 +1518,7 @@ func (x *RunUpdated) ProtoReflect() protoreflect.Message {
14431518
14441519 // Deprecated: Use RunUpdated.ProtoReflect.Descriptor instead.
14451520 func (*RunUpdated) Descriptor() ([]byte, []int) {
1446- return file_ermite_event_proto_rawDescGZIP(), []int{20}
1521+ return file_ermite_event_proto_rawDescGZIP(), []int{21}
14471522 }
14481523
14491524 func (x *RunUpdated) GetRun() *Run {
@@ -1465,7 +1540,7 @@ type JobUpdated struct {
14651540
14661541 func (x *JobUpdated) Reset() {
14671542 *x = JobUpdated{}
1468- mi := &file_ermite_event_proto_msgTypes[21]
1543+ mi := &file_ermite_event_proto_msgTypes[22]
14691544 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
14701545 ms.StoreMessageInfo(mi)
14711546 }
@@ -1477,7 +1552,7 @@ func (x *JobUpdated) String() string {
14771552 func (*JobUpdated) ProtoMessage() {}
14781553
14791554 func (x *JobUpdated) ProtoReflect() protoreflect.Message {
1480- mi := &file_ermite_event_proto_msgTypes[21]
1555+ mi := &file_ermite_event_proto_msgTypes[22]
14811556 if x != nil {
14821557 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
14831558 if ms.LoadMessageInfo() == nil {
@@ -1490,7 +1565,7 @@ func (x *JobUpdated) ProtoReflect() protoreflect.Message {
14901565
14911566 // Deprecated: Use JobUpdated.ProtoReflect.Descriptor instead.
14921567 func (*JobUpdated) Descriptor() ([]byte, []int) {
1493- return file_ermite_event_proto_rawDescGZIP(), []int{21}
1568+ return file_ermite_event_proto_rawDescGZIP(), []int{22}
14941569 }
14951570
14961571 func (x *JobUpdated) GetJob() *RunJob {
@@ -1509,7 +1584,7 @@ const file_ermite_event_proto_rawDesc = "" +
15091584 "\x06topics\x18\x01 \x03(\tR\x06topics\x12!\n" +
15101585 "\fresume_after\x18\x02 \x01(\tR\vresumeAfter\"4\n" +
15111586 "\rWatchResponse\x12#\n" +
1512- "\x05event\x18\x01 \x01(\v2\r.ermite.EventR\x05event\"\xa3\t\n" +
1587+ "\x05event\x18\x01 \x01(\v2\r.ermite.EventR\x05event\"\xe6\t\n" +
15131588 "\x05Event\x12\x0e\n" +
15141589 "\x02id\x18\x01 \x01(\tR\x02id\x12.\n" +
15151590 "\x04time\x18\x02 \x01(\v2\x1a.google.protobuf.TimestampR\x04time\x128\n" +
@@ -1533,7 +1608,8 @@ const file_ermite_event_proto_rawDesc = "" +
15331608 "\fissue_opened\x18\x11 \x01(\v2\x13.ermite.IssueOpenedH\x00R\vissueOpened\x128\n" +
15341609 "\fissue_closed\x18\x12 \x01(\v2\x13.ermite.IssueClosedH\x00R\vissueClosed\x12>\n" +
15351610 "\x0eissue_reopened\x18\x13 \x01(\v2\x15.ermite.IssueReopenedH\x00R\rissueReopened\x12A\n" +
1536- "\x0fissue_commented\x18\x14 \x01(\v2\x16.ermite.IssueCommentedH\x00R\x0eissueCommentedB\x06\n" +
1611+ "\x0fissue_commented\x18\x14 \x01(\v2\x16.ermite.IssueCommentedH\x00R\x0eissueCommented\x12A\n" +
1612+ "\x0fissue_submitted\x18\x15 \x01(\v2\x16.ermite.IssueSubmittedH\x00R\x0eissueSubmittedB\x06\n" +
15371613 "\x04kind\"/\n" +
15381614 "\vRepoCreated\x12 \n" +
15391615 "\x04repo\x18\x01 \x01(\v2\f.ermite.RepoR\x04repo\"/\n" +
@@ -1580,7 +1656,10 @@ const file_ermite_event_proto_rawDesc = "" +
15801656 "\x0eIssueCommented\x12\x12\n" +
15811657 "\x04repo\x18\x01 \x01(\tR\x04repo\x12\x16\n" +
15821658 "\x06number\x18\x02 \x01(\x03R\x06number\x12.\n" +
1583- "\acomment\x18\x03 \x01(\v2\x14.ermite.IssueCommentR\acomment\"\x05\n" +
1659+ "\acomment\x18\x03 \x01(\v2\x14.ermite.IssueCommentR\acomment\"4\n" +
1660+ "\x0eIssueSubmitted\x12\x12\n" +
1661+ "\x04repo\x18\x01 \x01(\tR\x04repo\x12\x0e\n" +
1662+ "\x02id\x18\x02 \x01(\tR\x02id\"\x05\n" +
15841663 "\x03Gap\"+\n" +
15851664 "\n" +
15861665 "RunUpdated\x12\x1d\n" +
@@ -1606,7 +1685,7 @@ func file_ermite_event_proto_rawDescGZIP() []byte {
16061685 return file_ermite_event_proto_rawDescData
16071686 }
16081687
1609-var file_ermite_event_proto_msgTypes = make([]protoimpl.MessageInfo, 22)
1688+var file_ermite_event_proto_msgTypes = make([]protoimpl.MessageInfo, 23)
16101689 var file_ermite_event_proto_goTypes = []any{
16111690 (*WatchRequest)(nil), // 0: ermite.WatchRequest
16121691 (*WatchResponse)(nil), // 1: ermite.WatchResponse
@@ -1627,32 +1706,33 @@ var file_ermite_event_proto_goTypes = []any{
16271706 (*IssueClosed)(nil), // 16: ermite.IssueClosed
16281707 (*IssueReopened)(nil), // 17: ermite.IssueReopened
16291708 (*IssueCommented)(nil), // 18: ermite.IssueCommented
1630- (*Gap)(nil), // 19: ermite.Gap
1631- (*RunUpdated)(nil), // 20: ermite.RunUpdated
1632- (*JobUpdated)(nil), // 21: ermite.JobUpdated
1633- (*timestamppb.Timestamp)(nil), // 22: google.protobuf.Timestamp
1634- (*Repo)(nil), // 23: ermite.Repo
1635- (*MergeRequest)(nil), // 24: ermite.MergeRequest
1636- (*Comment)(nil), // 25: ermite.Comment
1637- (*Issue)(nil), // 26: ermite.Issue
1638- (*IssueComment)(nil), // 27: ermite.IssueComment
1639- (*Run)(nil), // 28: ermite.Run
1640- (*RunJob)(nil), // 29: ermite.RunJob
1709+ (*IssueSubmitted)(nil), // 19: ermite.IssueSubmitted
1710+ (*Gap)(nil), // 20: ermite.Gap
1711+ (*RunUpdated)(nil), // 21: ermite.RunUpdated
1712+ (*JobUpdated)(nil), // 22: ermite.JobUpdated
1713+ (*timestamppb.Timestamp)(nil), // 23: google.protobuf.Timestamp
1714+ (*Repo)(nil), // 24: ermite.Repo
1715+ (*MergeRequest)(nil), // 25: ermite.MergeRequest
1716+ (*Comment)(nil), // 26: ermite.Comment
1717+ (*Issue)(nil), // 27: ermite.Issue
1718+ (*IssueComment)(nil), // 28: ermite.IssueComment
1719+ (*Run)(nil), // 29: ermite.Run
1720+ (*RunJob)(nil), // 30: ermite.RunJob
16411721 }
16421722 var file_ermite_event_proto_depIdxs = []int32{
16431723 2, // 0: ermite.WatchResponse.event:type_name -> ermite.Event
1644- 22, // 1: ermite.Event.time:type_name -> google.protobuf.Timestamp
1724+ 23, // 1: ermite.Event.time:type_name -> google.protobuf.Timestamp
16451725 3, // 2: ermite.Event.repo_created:type_name -> ermite.RepoCreated
16461726 4, // 3: ermite.Event.repo_updated:type_name -> ermite.RepoUpdated
16471727 5, // 4: ermite.Event.repo_deleted:type_name -> ermite.RepoDeleted
16481728 6, // 5: ermite.Event.pushed:type_name -> ermite.Pushed
1649- 19, // 6: ermite.Event.gap:type_name -> ermite.Gap
1729+ 20, // 6: ermite.Event.gap:type_name -> ermite.Gap
16501730 8, // 7: ermite.Event.merge_request_opened:type_name -> ermite.MergeRequestOpened
16511731 9, // 8: ermite.Event.merge_request_merged:type_name -> ermite.MergeRequestMerged
16521732 10, // 9: ermite.Event.merge_request_closed:type_name -> ermite.MergeRequestClosed
16531733 11, // 10: ermite.Event.comment_added:type_name -> ermite.CommentAdded
1654- 20, // 11: ermite.Event.run_updated:type_name -> ermite.RunUpdated
1655- 21, // 12: ermite.Event.job_updated:type_name -> ermite.JobUpdated
1734+ 21, // 11: ermite.Event.run_updated:type_name -> ermite.RunUpdated
1735+ 22, // 12: ermite.Event.job_updated:type_name -> ermite.JobUpdated
16561736 12, // 13: ermite.Event.comment_edited:type_name -> ermite.CommentEdited
16571737 13, // 14: ermite.Event.comment_deleted:type_name -> ermite.CommentDeleted
16581738 14, // 15: ermite.Event.comment_resolved:type_name -> ermite.CommentResolved
@@ -1660,29 +1740,30 @@ var file_ermite_event_proto_depIdxs = []int32{
16601740 16, // 17: ermite.Event.issue_closed:type_name -> ermite.IssueClosed
16611741 17, // 18: ermite.Event.issue_reopened:type_name -> ermite.IssueReopened
16621742 18, // 19: ermite.Event.issue_commented:type_name -> ermite.IssueCommented
1663- 23, // 20: ermite.RepoCreated.repo:type_name -> ermite.Repo
1664- 23, // 21: ermite.RepoUpdated.repo:type_name -> ermite.Repo
1665- 7, // 22: ermite.Pushed.ref_updates:type_name -> ermite.RefUpdate
1666- 24, // 23: ermite.MergeRequestOpened.merge_request:type_name -> ermite.MergeRequest
1667- 24, // 24: ermite.MergeRequestMerged.merge_request:type_name -> ermite.MergeRequest
1668- 24, // 25: ermite.MergeRequestClosed.merge_request:type_name -> ermite.MergeRequest
1669- 25, // 26: ermite.CommentAdded.comment:type_name -> ermite.Comment
1670- 25, // 27: ermite.CommentEdited.comment:type_name -> ermite.Comment
1671- 25, // 28: ermite.CommentDeleted.comment:type_name -> ermite.Comment
1672- 25, // 29: ermite.CommentResolved.comment:type_name -> ermite.Comment
1673- 26, // 30: ermite.IssueOpened.issue:type_name -> ermite.Issue
1674- 26, // 31: ermite.IssueClosed.issue:type_name -> ermite.Issue
1675- 26, // 32: ermite.IssueReopened.issue:type_name -> ermite.Issue
1676- 27, // 33: ermite.IssueCommented.comment:type_name -> ermite.IssueComment
1677- 28, // 34: ermite.RunUpdated.run:type_name -> ermite.Run
1678- 29, // 35: ermite.JobUpdated.job:type_name -> ermite.RunJob
1679- 0, // 36: ermite.EventService.Watch:input_type -> ermite.WatchRequest
1680- 1, // 37: ermite.EventService.Watch:output_type -> ermite.WatchResponse
1681- 37, // [37:38] is the sub-list for method output_type
1682- 36, // [36:37] is the sub-list for method input_type
1683- 36, // [36:36] is the sub-list for extension type_name
1684- 36, // [36:36] is the sub-list for extension extendee
1685- 0, // [0:36] is the sub-list for field type_name
1743+ 19, // 20: ermite.Event.issue_submitted:type_name -> ermite.IssueSubmitted
1744+ 24, // 21: ermite.RepoCreated.repo:type_name -> ermite.Repo
1745+ 24, // 22: ermite.RepoUpdated.repo:type_name -> ermite.Repo
1746+ 7, // 23: ermite.Pushed.ref_updates:type_name -> ermite.RefUpdate
1747+ 25, // 24: ermite.MergeRequestOpened.merge_request:type_name -> ermite.MergeRequest
1748+ 25, // 25: ermite.MergeRequestMerged.merge_request:type_name -> ermite.MergeRequest
1749+ 25, // 26: ermite.MergeRequestClosed.merge_request:type_name -> ermite.MergeRequest
1750+ 26, // 27: ermite.CommentAdded.comment:type_name -> ermite.Comment
1751+ 26, // 28: ermite.CommentEdited.comment:type_name -> ermite.Comment
1752+ 26, // 29: ermite.CommentDeleted.comment:type_name -> ermite.Comment
1753+ 26, // 30: ermite.CommentResolved.comment:type_name -> ermite.Comment
1754+ 27, // 31: ermite.IssueOpened.issue:type_name -> ermite.Issue
1755+ 27, // 32: ermite.IssueClosed.issue:type_name -> ermite.Issue
1756+ 27, // 33: ermite.IssueReopened.issue:type_name -> ermite.Issue
1757+ 28, // 34: ermite.IssueCommented.comment:type_name -> ermite.IssueComment
1758+ 29, // 35: ermite.RunUpdated.run:type_name -> ermite.Run
1759+ 30, // 36: ermite.JobUpdated.job:type_name -> ermite.RunJob
1760+ 0, // 37: ermite.EventService.Watch:input_type -> ermite.WatchRequest
1761+ 1, // 38: ermite.EventService.Watch:output_type -> ermite.WatchResponse
1762+ 38, // [38:39] is the sub-list for method output_type
1763+ 37, // [37:38] is the sub-list for method input_type
1764+ 37, // [37:37] is the sub-list for extension type_name
1765+ 37, // [37:37] is the sub-list for extension extendee
1766+ 0, // [0:37] is the sub-list for field type_name
16861767 }
16871768
16881769 func init() { file_ermite_event_proto_init() }
@@ -1713,6 +1794,7 @@ func file_ermite_event_proto_init() {
17131794 (*Event_IssueClosed)(nil),
17141795 (*Event_IssueReopened)(nil),
17151796 (*Event_IssueCommented)(nil),
1797+ (*Event_IssueSubmitted)(nil),
17161798 }
17171799 type x struct{}
17181800 out := protoimpl.TypeBuilder{
@@ -1720,7 +1802,7 @@ func file_ermite_event_proto_init() {
17201802 GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
17211803 RawDescriptor: unsafe.Slice(unsafe.StringData(file_ermite_event_proto_rawDesc), len(file_ermite_event_proto_rawDesc)),
17221804 NumEnums: 0,
1723- NumMessages: 22,
1805+ NumMessages: 23,
17241806 NumExtensions: 0,
17251807 NumServices: 1,
17261808 },
@@ -161,6 +161,7 @@ type Event struct {
161 // *Event_IssueClosed161 // *Event_IssueClosed
162 // *Event_IssueReopened162 // *Event_IssueReopened
163 // *Event_IssueCommented163 // *Event_IssueCommented
164+ // *Event_IssueSubmitted
164 Kind isEvent_Kind `protobuf_oneof:"kind"`165 Kind isEvent_Kind `protobuf_oneof:"kind"`
165 unknownFields protoimpl.UnknownFields166 unknownFields protoimpl.UnknownFields
166 sizeCache protoimpl.SizeCache167 sizeCache protoimpl.SizeCache
@@ -379,6 +380,15 @@ func (x *Event) GetIssueCommented() *IssueCommented {
379 return nil380 return nil
380 }381 }
381 382
383+func (x *Event) GetIssueSubmitted() *IssueSubmitted {
384+ if x != nil {
385+ if x, ok := x.Kind.(*Event_IssueSubmitted); ok {
386+ return x.IssueSubmitted
387+ }
388+ }
389+ return nil
390+}
391+
382 type isEvent_Kind interface {392 type isEvent_Kind interface {
383 isEvent_Kind()393 isEvent_Kind()
384 }394 }
@@ -473,6 +483,12 @@ type Event_IssueCommented struct {
473 IssueCommented *IssueCommented `protobuf:"bytes,20,opt,name=issue_commented,json=issueCommented,proto3,oneof"`483 IssueCommented *IssueCommented `protobuf:"bytes,20,opt,name=issue_commented,json=issueCommented,proto3,oneof"`
474 }484 }
475 485
486+type Event_IssueSubmitted struct {
487+ // IssueSubmitted: an issue is awaiting moderation. Delivered to owner
488+ // watchers only.
489+ IssueSubmitted *IssueSubmitted `protobuf:"bytes,21,opt,name=issue_submitted,json=issueSubmitted,proto3,oneof"`
490+}
491+
476 func (*Event_RepoCreated) isEvent_Kind() {}492 func (*Event_RepoCreated) isEvent_Kind() {}
477 493
478 func (*Event_RepoUpdated) isEvent_Kind() {}494 func (*Event_RepoUpdated) isEvent_Kind() {}
@@ -509,6 +525,8 @@ func (*Event_IssueReopened) isEvent_Kind() {}
509 525
510 func (*Event_IssueCommented) isEvent_Kind() {}526 func (*Event_IssueCommented) isEvent_Kind() {}
511 527
528+func (*Event_IssueSubmitted) isEvent_Kind() {}
529+
512 // RepoCreated reports a new repository.530 // RepoCreated reports a new repository.
513 type RepoCreated struct {531 type RepoCreated struct {
514 state protoimpl.MessageState `protogen:"open.v1"`532 state protoimpl.MessageState `protogen:"open.v1"`
@@ -1365,6 +1383,63 @@ func (x *IssueCommented) GetComment() *IssueComment {
1365 return nil1383 return nil
1366 }1384 }
1367 1385
1386+// IssueSubmitted reports an issue awaiting moderation. It is delivered to
1387+// owner watchers only. It carries no title or body: content never travels in
1388+// events, read it with IssueService.ListPendingIssues.
1389+type IssueSubmitted struct {
1390+ state protoimpl.MessageState `protogen:"open.v1"`
1391+ // Repo is the name of the repository.
1392+ Repo string `protobuf:"bytes,1,opt,name=repo,proto3" json:"repo,omitempty"`
1393+ // Id is the PendingIssue.id.
1394+ Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"`
1395+ unknownFields protoimpl.UnknownFields
1396+ sizeCache protoimpl.SizeCache
1397+}
1398+
1399+func (x *IssueSubmitted) Reset() {
1400+ *x = IssueSubmitted{}
1401+ mi := &file_ermite_event_proto_msgTypes[19]
1402+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
1403+ ms.StoreMessageInfo(mi)
1404+}
1405+
1406+func (x *IssueSubmitted) String() string {
1407+ return protoimpl.X.MessageStringOf(x)
1408+}
1409+
1410+func (*IssueSubmitted) ProtoMessage() {}
1411+
1412+func (x *IssueSubmitted) ProtoReflect() protoreflect.Message {
1413+ mi := &file_ermite_event_proto_msgTypes[19]
1414+ if x != nil {
1415+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
1416+ if ms.LoadMessageInfo() == nil {
1417+ ms.StoreMessageInfo(mi)
1418+ }
1419+ return ms
1420+ }
1421+ return mi.MessageOf(x)
1422+}
1423+
1424+// Deprecated: Use IssueSubmitted.ProtoReflect.Descriptor instead.
1425+func (*IssueSubmitted) Descriptor() ([]byte, []int) {
1426+ return file_ermite_event_proto_rawDescGZIP(), []int{19}
1427+}
1428+
1429+func (x *IssueSubmitted) GetRepo() string {
1430+ if x != nil {
1431+ return x.Repo
1432+ }
1433+ return ""
1434+}
1435+
1436+func (x *IssueSubmitted) GetId() string {
1437+ if x != nil {
1438+ return x.Id
1439+ }
1440+ return ""
1441+}
1442+
1368 // Gap tells the client that events were lost: the requested resume point is1443 // Gap tells the client that events were lost: the requested resume point is
1369 // older than the server's buffer. It has no fields. The client should reload1444 // older than the server's buffer. It has no fields. The client should reload
1370 // the state it displays (repositories, refs, merge requests and their1445 // the state it displays (repositories, refs, merge requests and their
@@ -1378,7 +1453,7 @@ type Gap struct {
1378 1453
1379 func (x *Gap) Reset() {1454 func (x *Gap) Reset() {
1380 *x = Gap{}1455 *x = Gap{}
1381- mi := &file_ermite_event_proto_msgTypes[19]1456+ mi := &file_ermite_event_proto_msgTypes[20]
1382 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))1457 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
1383 ms.StoreMessageInfo(mi)1458 ms.StoreMessageInfo(mi)
1384 }1459 }
@@ -1390,7 +1465,7 @@ func (x *Gap) String() string {
1390 func (*Gap) ProtoMessage() {}1465 func (*Gap) ProtoMessage() {}
1391 1466
1392 func (x *Gap) ProtoReflect() protoreflect.Message {1467 func (x *Gap) ProtoReflect() protoreflect.Message {
1393- mi := &file_ermite_event_proto_msgTypes[19]1468+ mi := &file_ermite_event_proto_msgTypes[20]
1394 if x != nil {1469 if x != nil {
1395 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))1470 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
1396 if ms.LoadMessageInfo() == nil {1471 if ms.LoadMessageInfo() == nil {
@@ -1403,7 +1478,7 @@ func (x *Gap) ProtoReflect() protoreflect.Message {
1403 1478
1404 // Deprecated: Use Gap.ProtoReflect.Descriptor instead.1479 // Deprecated: Use Gap.ProtoReflect.Descriptor instead.
1405 func (*Gap) Descriptor() ([]byte, []int) {1480 func (*Gap) Descriptor() ([]byte, []int) {
1406- return file_ermite_event_proto_rawDescGZIP(), []int{19}1481+ return file_ermite_event_proto_rawDescGZIP(), []int{20}
1407 }1482 }
1408 1483
1409 // RunUpdated reports a CI run that was created or whose status, conclusion or1484 // RunUpdated reports a CI run that was created or whose status, conclusion or
@@ -1418,7 +1493,7 @@ type RunUpdated struct {
1418 1493
1419 func (x *RunUpdated) Reset() {1494 func (x *RunUpdated) Reset() {
1420 *x = RunUpdated{}1495 *x = RunUpdated{}
1421- mi := &file_ermite_event_proto_msgTypes[20]1496+ mi := &file_ermite_event_proto_msgTypes[21]
1422 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))1497 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
1423 ms.StoreMessageInfo(mi)1498 ms.StoreMessageInfo(mi)
1424 }1499 }
@@ -1430,7 +1505,7 @@ func (x *RunUpdated) String() string {
1430 func (*RunUpdated) ProtoMessage() {}1505 func (*RunUpdated) ProtoMessage() {}
1431 1506
1432 func (x *RunUpdated) ProtoReflect() protoreflect.Message {1507 func (x *RunUpdated) ProtoReflect() protoreflect.Message {
1433- mi := &file_ermite_event_proto_msgTypes[20]1508+ mi := &file_ermite_event_proto_msgTypes[21]
1434 if x != nil {1509 if x != nil {
1435 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))1510 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
1436 if ms.LoadMessageInfo() == nil {1511 if ms.LoadMessageInfo() == nil {
@@ -1443,7 +1518,7 @@ func (x *RunUpdated) ProtoReflect() protoreflect.Message {
1443 1518
1444 // Deprecated: Use RunUpdated.ProtoReflect.Descriptor instead.1519 // Deprecated: Use RunUpdated.ProtoReflect.Descriptor instead.
1445 func (*RunUpdated) Descriptor() ([]byte, []int) {1520 func (*RunUpdated) Descriptor() ([]byte, []int) {
1446- return file_ermite_event_proto_rawDescGZIP(), []int{20}1521+ return file_ermite_event_proto_rawDescGZIP(), []int{21}
1447 }1522 }
1448 1523
1449 func (x *RunUpdated) GetRun() *Run {1524 func (x *RunUpdated) GetRun() *Run {
@@ -1465,7 +1540,7 @@ type JobUpdated struct {
1465 1540
1466 func (x *JobUpdated) Reset() {1541 func (x *JobUpdated) Reset() {
1467 *x = JobUpdated{}1542 *x = JobUpdated{}
1468- mi := &file_ermite_event_proto_msgTypes[21]1543+ mi := &file_ermite_event_proto_msgTypes[22]
1469 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))1544 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
1470 ms.StoreMessageInfo(mi)1545 ms.StoreMessageInfo(mi)
1471 }1546 }
@@ -1477,7 +1552,7 @@ func (x *JobUpdated) String() string {
1477 func (*JobUpdated) ProtoMessage() {}1552 func (*JobUpdated) ProtoMessage() {}
1478 1553
1479 func (x *JobUpdated) ProtoReflect() protoreflect.Message {1554 func (x *JobUpdated) ProtoReflect() protoreflect.Message {
1480- mi := &file_ermite_event_proto_msgTypes[21]1555+ mi := &file_ermite_event_proto_msgTypes[22]
1481 if x != nil {1556 if x != nil {
1482 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))1557 ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
1483 if ms.LoadMessageInfo() == nil {1558 if ms.LoadMessageInfo() == nil {
@@ -1490,7 +1565,7 @@ func (x *JobUpdated) ProtoReflect() protoreflect.Message {
1490 1565
1491 // Deprecated: Use JobUpdated.ProtoReflect.Descriptor instead.1566 // Deprecated: Use JobUpdated.ProtoReflect.Descriptor instead.
1492 func (*JobUpdated) Descriptor() ([]byte, []int) {1567 func (*JobUpdated) Descriptor() ([]byte, []int) {
1493- return file_ermite_event_proto_rawDescGZIP(), []int{21}1568+ return file_ermite_event_proto_rawDescGZIP(), []int{22}
1494 }1569 }
1495 1570
1496 func (x *JobUpdated) GetJob() *RunJob {1571 func (x *JobUpdated) GetJob() *RunJob {
@@ -1509,7 +1584,7 @@ const file_ermite_event_proto_rawDesc = "" +
1509 "\x06topics\x18\x01 \x03(\tR\x06topics\x12!\n" +1584 "\x06topics\x18\x01 \x03(\tR\x06topics\x12!\n" +
1510 "\fresume_after\x18\x02 \x01(\tR\vresumeAfter\"4\n" +1585 "\fresume_after\x18\x02 \x01(\tR\vresumeAfter\"4\n" +
1511 "\rWatchResponse\x12#\n" +1586 "\rWatchResponse\x12#\n" +
1512- "\x05event\x18\x01 \x01(\v2\r.ermite.EventR\x05event\"\xa3\t\n" +1587+ "\x05event\x18\x01 \x01(\v2\r.ermite.EventR\x05event\"\xe6\t\n" +
1513 "\x05Event\x12\x0e\n" +1588 "\x05Event\x12\x0e\n" +
1514 "\x02id\x18\x01 \x01(\tR\x02id\x12.\n" +1589 "\x02id\x18\x01 \x01(\tR\x02id\x12.\n" +
1515 "\x04time\x18\x02 \x01(\v2\x1a.google.protobuf.TimestampR\x04time\x128\n" +1590 "\x04time\x18\x02 \x01(\v2\x1a.google.protobuf.TimestampR\x04time\x128\n" +
@@ -1533,7 +1608,8 @@ const file_ermite_event_proto_rawDesc = "" +
1533 "\fissue_opened\x18\x11 \x01(\v2\x13.ermite.IssueOpenedH\x00R\vissueOpened\x128\n" +1608 "\fissue_opened\x18\x11 \x01(\v2\x13.ermite.IssueOpenedH\x00R\vissueOpened\x128\n" +
1534 "\fissue_closed\x18\x12 \x01(\v2\x13.ermite.IssueClosedH\x00R\vissueClosed\x12>\n" +1609 "\fissue_closed\x18\x12 \x01(\v2\x13.ermite.IssueClosedH\x00R\vissueClosed\x12>\n" +
1535 "\x0eissue_reopened\x18\x13 \x01(\v2\x15.ermite.IssueReopenedH\x00R\rissueReopened\x12A\n" +1610 "\x0eissue_reopened\x18\x13 \x01(\v2\x15.ermite.IssueReopenedH\x00R\rissueReopened\x12A\n" +
1536- "\x0fissue_commented\x18\x14 \x01(\v2\x16.ermite.IssueCommentedH\x00R\x0eissueCommentedB\x06\n" +1611+ "\x0fissue_commented\x18\x14 \x01(\v2\x16.ermite.IssueCommentedH\x00R\x0eissueCommented\x12A\n" +
1612+ "\x0fissue_submitted\x18\x15 \x01(\v2\x16.ermite.IssueSubmittedH\x00R\x0eissueSubmittedB\x06\n" +
1537 "\x04kind\"/\n" +1613 "\x04kind\"/\n" +
1538 "\vRepoCreated\x12 \n" +1614 "\vRepoCreated\x12 \n" +
1539 "\x04repo\x18\x01 \x01(\v2\f.ermite.RepoR\x04repo\"/\n" +1615 "\x04repo\x18\x01 \x01(\v2\f.ermite.RepoR\x04repo\"/\n" +
@@ -1580,7 +1656,10 @@ const file_ermite_event_proto_rawDesc = "" +
1580 "\x0eIssueCommented\x12\x12\n" +1656 "\x0eIssueCommented\x12\x12\n" +
1581 "\x04repo\x18\x01 \x01(\tR\x04repo\x12\x16\n" +1657 "\x04repo\x18\x01 \x01(\tR\x04repo\x12\x16\n" +
1582 "\x06number\x18\x02 \x01(\x03R\x06number\x12.\n" +1658 "\x06number\x18\x02 \x01(\x03R\x06number\x12.\n" +
1583- "\acomment\x18\x03 \x01(\v2\x14.ermite.IssueCommentR\acomment\"\x05\n" +1659+ "\acomment\x18\x03 \x01(\v2\x14.ermite.IssueCommentR\acomment\"4\n" +
1660+ "\x0eIssueSubmitted\x12\x12\n" +
1661+ "\x04repo\x18\x01 \x01(\tR\x04repo\x12\x0e\n" +
1662+ "\x02id\x18\x02 \x01(\tR\x02id\"\x05\n" +
1584 "\x03Gap\"+\n" +1663 "\x03Gap\"+\n" +
1585 "\n" +1664 "\n" +
1586 "RunUpdated\x12\x1d\n" +1665 "RunUpdated\x12\x1d\n" +
@@ -1606,7 +1685,7 @@ func file_ermite_event_proto_rawDescGZIP() []byte {
1606 return file_ermite_event_proto_rawDescData1685 return file_ermite_event_proto_rawDescData
1607 }1686 }
1608 1687
1609-var file_ermite_event_proto_msgTypes = make([]protoimpl.MessageInfo, 22)1688+var file_ermite_event_proto_msgTypes = make([]protoimpl.MessageInfo, 23)
1610 var file_ermite_event_proto_goTypes = []any{1689 var file_ermite_event_proto_goTypes = []any{
1611 (*WatchRequest)(nil), // 0: ermite.WatchRequest1690 (*WatchRequest)(nil), // 0: ermite.WatchRequest
1612 (*WatchResponse)(nil), // 1: ermite.WatchResponse1691 (*WatchResponse)(nil), // 1: ermite.WatchResponse
@@ -1627,32 +1706,33 @@ var file_ermite_event_proto_goTypes = []any{
1627 (*IssueClosed)(nil), // 16: ermite.IssueClosed1706 (*IssueClosed)(nil), // 16: ermite.IssueClosed
1628 (*IssueReopened)(nil), // 17: ermite.IssueReopened1707 (*IssueReopened)(nil), // 17: ermite.IssueReopened
1629 (*IssueCommented)(nil), // 18: ermite.IssueCommented1708 (*IssueCommented)(nil), // 18: ermite.IssueCommented
1630- (*Gap)(nil), // 19: ermite.Gap1709+ (*IssueSubmitted)(nil), // 19: ermite.IssueSubmitted
1631- (*RunUpdated)(nil), // 20: ermite.RunUpdated1710+ (*Gap)(nil), // 20: ermite.Gap
1632- (*JobUpdated)(nil), // 21: ermite.JobUpdated1711+ (*RunUpdated)(nil), // 21: ermite.RunUpdated
1633- (*timestamppb.Timestamp)(nil), // 22: google.protobuf.Timestamp1712+ (*JobUpdated)(nil), // 22: ermite.JobUpdated
1634- (*Repo)(nil), // 23: ermite.Repo1713+ (*timestamppb.Timestamp)(nil), // 23: google.protobuf.Timestamp
1635- (*MergeRequest)(nil), // 24: ermite.MergeRequest1714+ (*Repo)(nil), // 24: ermite.Repo
1636- (*Comment)(nil), // 25: ermite.Comment1715+ (*MergeRequest)(nil), // 25: ermite.MergeRequest
1637- (*Issue)(nil), // 26: ermite.Issue1716+ (*Comment)(nil), // 26: ermite.Comment
1638- (*IssueComment)(nil), // 27: ermite.IssueComment1717+ (*Issue)(nil), // 27: ermite.Issue
1639- (*Run)(nil), // 28: ermite.Run1718+ (*IssueComment)(nil), // 28: ermite.IssueComment
1640- (*RunJob)(nil), // 29: ermite.RunJob1719+ (*Run)(nil), // 29: ermite.Run
1720+ (*RunJob)(nil), // 30: ermite.RunJob
1641 }1721 }
1642 var file_ermite_event_proto_depIdxs = []int32{1722 var file_ermite_event_proto_depIdxs = []int32{
1643 2, // 0: ermite.WatchResponse.event:type_name -> ermite.Event1723 2, // 0: ermite.WatchResponse.event:type_name -> ermite.Event
1644- 22, // 1: ermite.Event.time:type_name -> google.protobuf.Timestamp1724+ 23, // 1: ermite.Event.time:type_name -> google.protobuf.Timestamp
1645 3, // 2: ermite.Event.repo_created:type_name -> ermite.RepoCreated1725 3, // 2: ermite.Event.repo_created:type_name -> ermite.RepoCreated
1646 4, // 3: ermite.Event.repo_updated:type_name -> ermite.RepoUpdated1726 4, // 3: ermite.Event.repo_updated:type_name -> ermite.RepoUpdated
1647 5, // 4: ermite.Event.repo_deleted:type_name -> ermite.RepoDeleted1727 5, // 4: ermite.Event.repo_deleted:type_name -> ermite.RepoDeleted
1648 6, // 5: ermite.Event.pushed:type_name -> ermite.Pushed1728 6, // 5: ermite.Event.pushed:type_name -> ermite.Pushed
1649- 19, // 6: ermite.Event.gap:type_name -> ermite.Gap1729+ 20, // 6: ermite.Event.gap:type_name -> ermite.Gap
1650 8, // 7: ermite.Event.merge_request_opened:type_name -> ermite.MergeRequestOpened1730 8, // 7: ermite.Event.merge_request_opened:type_name -> ermite.MergeRequestOpened
1651 9, // 8: ermite.Event.merge_request_merged:type_name -> ermite.MergeRequestMerged1731 9, // 8: ermite.Event.merge_request_merged:type_name -> ermite.MergeRequestMerged
1652 10, // 9: ermite.Event.merge_request_closed:type_name -> ermite.MergeRequestClosed1732 10, // 9: ermite.Event.merge_request_closed:type_name -> ermite.MergeRequestClosed
1653 11, // 10: ermite.Event.comment_added:type_name -> ermite.CommentAdded1733 11, // 10: ermite.Event.comment_added:type_name -> ermite.CommentAdded
1654- 20, // 11: ermite.Event.run_updated:type_name -> ermite.RunUpdated1734+ 21, // 11: ermite.Event.run_updated:type_name -> ermite.RunUpdated
1655- 21, // 12: ermite.Event.job_updated:type_name -> ermite.JobUpdated1735+ 22, // 12: ermite.Event.job_updated:type_name -> ermite.JobUpdated
1656 12, // 13: ermite.Event.comment_edited:type_name -> ermite.CommentEdited1736 12, // 13: ermite.Event.comment_edited:type_name -> ermite.CommentEdited
1657 13, // 14: ermite.Event.comment_deleted:type_name -> ermite.CommentDeleted1737 13, // 14: ermite.Event.comment_deleted:type_name -> ermite.CommentDeleted
1658 14, // 15: ermite.Event.comment_resolved:type_name -> ermite.CommentResolved1738 14, // 15: ermite.Event.comment_resolved:type_name -> ermite.CommentResolved
@@ -1660,29 +1740,30 @@ var file_ermite_event_proto_depIdxs = []int32{
1660 16, // 17: ermite.Event.issue_closed:type_name -> ermite.IssueClosed1740 16, // 17: ermite.Event.issue_closed:type_name -> ermite.IssueClosed
1661 17, // 18: ermite.Event.issue_reopened:type_name -> ermite.IssueReopened1741 17, // 18: ermite.Event.issue_reopened:type_name -> ermite.IssueReopened
1662 18, // 19: ermite.Event.issue_commented:type_name -> ermite.IssueCommented1742 18, // 19: ermite.Event.issue_commented:type_name -> ermite.IssueCommented
1663- 23, // 20: ermite.RepoCreated.repo:type_name -> ermite.Repo1743+ 19, // 20: ermite.Event.issue_submitted:type_name -> ermite.IssueSubmitted
1664- 23, // 21: ermite.RepoUpdated.repo:type_name -> ermite.Repo1744+ 24, // 21: ermite.RepoCreated.repo:type_name -> ermite.Repo
1665- 7, // 22: ermite.Pushed.ref_updates:type_name -> ermite.RefUpdate1745+ 24, // 22: ermite.RepoUpdated.repo:type_name -> ermite.Repo
1666- 24, // 23: ermite.MergeRequestOpened.merge_request:type_name -> ermite.MergeRequest1746+ 7, // 23: ermite.Pushed.ref_updates:type_name -> ermite.RefUpdate
1667- 24, // 24: ermite.MergeRequestMerged.merge_request:type_name -> ermite.MergeRequest1747+ 25, // 24: ermite.MergeRequestOpened.merge_request:type_name -> ermite.MergeRequest
1668- 24, // 25: ermite.MergeRequestClosed.merge_request:type_name -> ermite.MergeRequest1748+ 25, // 25: ermite.MergeRequestMerged.merge_request:type_name -> ermite.MergeRequest
1669- 25, // 26: ermite.CommentAdded.comment:type_name -> ermite.Comment1749+ 25, // 26: ermite.MergeRequestClosed.merge_request:type_name -> ermite.MergeRequest
1670- 25, // 27: ermite.CommentEdited.comment:type_name -> ermite.Comment1750+ 26, // 27: ermite.CommentAdded.comment:type_name -> ermite.Comment
1671- 25, // 28: ermite.CommentDeleted.comment:type_name -> ermite.Comment1751+ 26, // 28: ermite.CommentEdited.comment:type_name -> ermite.Comment
1672- 25, // 29: ermite.CommentResolved.comment:type_name -> ermite.Comment1752+ 26, // 29: ermite.CommentDeleted.comment:type_name -> ermite.Comment
1673- 26, // 30: ermite.IssueOpened.issue:type_name -> ermite.Issue1753+ 26, // 30: ermite.CommentResolved.comment:type_name -> ermite.Comment
1674- 26, // 31: ermite.IssueClosed.issue:type_name -> ermite.Issue1754+ 27, // 31: ermite.IssueOpened.issue:type_name -> ermite.Issue
1675- 26, // 32: ermite.IssueReopened.issue:type_name -> ermite.Issue1755+ 27, // 32: ermite.IssueClosed.issue:type_name -> ermite.Issue
1676- 27, // 33: ermite.IssueCommented.comment:type_name -> ermite.IssueComment1756+ 27, // 33: ermite.IssueReopened.issue:type_name -> ermite.Issue
1677- 28, // 34: ermite.RunUpdated.run:type_name -> ermite.Run1757+ 28, // 34: ermite.IssueCommented.comment:type_name -> ermite.IssueComment
1678- 29, // 35: ermite.JobUpdated.job:type_name -> ermite.RunJob1758+ 29, // 35: ermite.RunUpdated.run:type_name -> ermite.Run
1679- 0, // 36: ermite.EventService.Watch:input_type -> ermite.WatchRequest1759+ 30, // 36: ermite.JobUpdated.job:type_name -> ermite.RunJob
1680- 1, // 37: ermite.EventService.Watch:output_type -> ermite.WatchResponse1760+ 0, // 37: ermite.EventService.Watch:input_type -> ermite.WatchRequest
1681- 37, // [37:38] is the sub-list for method output_type1761+ 1, // 38: ermite.EventService.Watch:output_type -> ermite.WatchResponse
1682- 36, // [36:37] is the sub-list for method input_type1762+ 38, // [38:39] is the sub-list for method output_type
1683- 36, // [36:36] is the sub-list for extension type_name1763+ 37, // [37:38] is the sub-list for method input_type
1684- 36, // [36:36] is the sub-list for extension extendee1764+ 37, // [37:37] is the sub-list for extension type_name
1685- 0, // [0:36] is the sub-list for field type_name1765+ 37, // [37:37] is the sub-list for extension extendee
1766+ 0, // [0:37] is the sub-list for field type_name
1686 }1767 }
1687 1768
1688 func init() { file_ermite_event_proto_init() }1769 func init() { file_ermite_event_proto_init() }
@@ -1713,6 +1794,7 @@ func file_ermite_event_proto_init() {
1713 (*Event_IssueClosed)(nil),1794 (*Event_IssueClosed)(nil),
1714 (*Event_IssueReopened)(nil),1795 (*Event_IssueReopened)(nil),
1715 (*Event_IssueCommented)(nil),1796 (*Event_IssueCommented)(nil),
1797+ (*Event_IssueSubmitted)(nil),
1716 }1798 }
1717 type x struct{}1799 type x struct{}
1718 out := protoimpl.TypeBuilder{1800 out := protoimpl.TypeBuilder{
@@ -1720,7 +1802,7 @@ func file_ermite_event_proto_init() {
1720 GoPackagePath: reflect.TypeOf(x{}).PkgPath(),1802 GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
1721 RawDescriptor: unsafe.Slice(unsafe.StringData(file_ermite_event_proto_rawDesc), len(file_ermite_event_proto_rawDesc)),1803 RawDescriptor: unsafe.Slice(unsafe.StringData(file_ermite_event_proto_rawDesc), len(file_ermite_event_proto_rawDesc)),
1722 NumEnums: 0,1804 NumEnums: 0,
1723- NumMessages: 22,1805+ NumMessages: 23,
1724 NumExtensions: 0,1806 NumExtensions: 0,
1725 NumServices: 1,1807 NumServices: 1,
1726 },1808 },
M gen/ermite/issue.pb.go +503 -41 generated file: collapsed
M issue_test.go +20 -7
@@ -13,12 +13,15 @@ import (
1313
1414 func TestIssueProcedures(t *testing.T) {
1515 tests := map[string]string{
16- ermiteconnect.IssueServiceCreateIssueProcedure: "CreateIssue",
17- ermiteconnect.IssueServiceListIssuesProcedure: "ListIssues",
18- ermiteconnect.IssueServiceGetIssueProcedure: "GetIssue",
19- ermiteconnect.IssueServiceAddIssueCommentProcedure: "AddIssueComment",
20- ermiteconnect.IssueServiceCloseIssueProcedure: "CloseIssue",
21- ermiteconnect.IssueServiceReopenIssueProcedure: "ReopenIssue",
16+ ermiteconnect.IssueServiceCreateIssueProcedure: "CreateIssue",
17+ ermiteconnect.IssueServiceListIssuesProcedure: "ListIssues",
18+ ermiteconnect.IssueServiceGetIssueProcedure: "GetIssue",
19+ ermiteconnect.IssueServiceAddIssueCommentProcedure: "AddIssueComment",
20+ ermiteconnect.IssueServiceCloseIssueProcedure: "CloseIssue",
21+ ermiteconnect.IssueServiceReopenIssueProcedure: "ReopenIssue",
22+ ermiteconnect.IssueServiceListPendingIssuesProcedure: "ListPendingIssues",
23+ ermiteconnect.IssueServiceApproveIssueProcedure: "ApproveIssue",
24+ ermiteconnect.IssueServiceRejectIssueProcedure: "RejectIssue",
2225 }
2326 for got, name := range tests {
2427 if want := "/ermite.IssueService/" + name; got != want {
@@ -36,7 +39,14 @@ func TestIssueContract(t *testing.T) {
3639 {&ermite.IssueComment{}, map[string]int{"id": 1, "author": 2, "text": 3, "created_at": 4}},
3740 {&ermite.RepoSettings{}, map[string]int{"issues": 3}},
3841 {&ermite.UpdateRepoRequest{}, map[string]int{"issues": 8}},
39- {&ermite.Event{}, map[string]int{"issue_opened": 17, "issue_closed": 18, "issue_reopened": 19, "issue_commented": 20}},
42+ {&ermite.Event{}, map[string]int{"issue_opened": 17, "issue_closed": 18, "issue_reopened": 19, "issue_commented": 20, "issue_submitted": 21}},
43+ {&ermite.IssueSubmitted{}, map[string]int{"repo": 1, "id": 2}},
44+ {&ermite.PendingIssue{}, map[string]int{"id": 1, "repo": 2, "title": 3, "body": 4, "author": 5, "created_at": 6}},
45+ {&ermite.ListPendingIssuesRequest{}, map[string]int{"repo": 1, "page_size": 2, "page_token": 3}},
46+ {&ermite.ListPendingIssuesResponse{}, map[string]int{"issues": 1, "next_page_token": 2}},
47+ {&ermite.ApproveIssueRequest{}, map[string]int{"repo": 1, "id": 2}},
48+ {&ermite.ApproveIssueResponse{}, map[string]int{"issue": 1}},
49+ {&ermite.RejectIssueRequest{}, map[string]int{"repo": 1, "id": 2, "ban": 3}},
4050 } {
4151 md := tt.msg.ProtoReflect().Descriptor()
4252 for name, num := range tt.fields {
@@ -64,6 +74,9 @@ func TestIssueContract(t *testing.T) {
6474 &ermite.Event{Kind: &ermite.Event_IssueOpened{IssueOpened: &ermite.IssueOpened{Issue: &ermite.Issue{Repo: "r", Number: 1}}}},
6575 &ermite.Event{Kind: &ermite.Event_IssueClosed{IssueClosed: &ermite.IssueClosed{Issue: &ermite.Issue{Repo: "r", Number: 1}}}},
6676 &ermite.Event{Kind: &ermite.Event_IssueReopened{IssueReopened: &ermite.IssueReopened{Issue: &ermite.Issue{Repo: "r", Number: 1}}}},
77+ &ermite.Event{Kind: &ermite.Event_IssueSubmitted{IssueSubmitted: &ermite.IssueSubmitted{Repo: "r", Id: "p1"}}},
78+ &ermite.PendingIssue{Id: "p1", Repo: "r", Title: "t", Author: "anon"},
79+ &ermite.RejectIssueRequest{Repo: "r", Id: "p1", Ban: true},
6780 &ermite.Event{Kind: &ermite.Event_IssueCommented{IssueCommented: &ermite.IssueCommented{Repo: "r", Number: 1, Comment: &ermite.IssueComment{Id: "c", Text: "hi"}}}},
6881 } {
6982 b, err := proto.Marshal(in)
@@ -13,12 +13,15 @@ import (
13 13
14 func TestIssueProcedures(t *testing.T) {14 func TestIssueProcedures(t *testing.T) {
15 tests := map[string]string{15 tests := map[string]string{
16- ermiteconnect.IssueServiceCreateIssueProcedure: "CreateIssue",16+ ermiteconnect.IssueServiceCreateIssueProcedure: "CreateIssue",
17- ermiteconnect.IssueServiceListIssuesProcedure: "ListIssues",17+ ermiteconnect.IssueServiceListIssuesProcedure: "ListIssues",
18- ermiteconnect.IssueServiceGetIssueProcedure: "GetIssue",18+ ermiteconnect.IssueServiceGetIssueProcedure: "GetIssue",
19- ermiteconnect.IssueServiceAddIssueCommentProcedure: "AddIssueComment",19+ ermiteconnect.IssueServiceAddIssueCommentProcedure: "AddIssueComment",
20- ermiteconnect.IssueServiceCloseIssueProcedure: "CloseIssue",20+ ermiteconnect.IssueServiceCloseIssueProcedure: "CloseIssue",
21- ermiteconnect.IssueServiceReopenIssueProcedure: "ReopenIssue",21+ ermiteconnect.IssueServiceReopenIssueProcedure: "ReopenIssue",
22+ ermiteconnect.IssueServiceListPendingIssuesProcedure: "ListPendingIssues",
23+ ermiteconnect.IssueServiceApproveIssueProcedure: "ApproveIssue",
24+ ermiteconnect.IssueServiceRejectIssueProcedure: "RejectIssue",
22 }25 }
23 for got, name := range tests {26 for got, name := range tests {
24 if want := "/ermite.IssueService/" + name; got != want {27 if want := "/ermite.IssueService/" + name; got != want {
@@ -36,7 +39,14 @@ func TestIssueContract(t *testing.T) {
36 {&ermite.IssueComment{}, map[string]int{"id": 1, "author": 2, "text": 3, "created_at": 4}},39 {&ermite.IssueComment{}, map[string]int{"id": 1, "author": 2, "text": 3, "created_at": 4}},
37 {&ermite.RepoSettings{}, map[string]int{"issues": 3}},40 {&ermite.RepoSettings{}, map[string]int{"issues": 3}},
38 {&ermite.UpdateRepoRequest{}, map[string]int{"issues": 8}},41 {&ermite.UpdateRepoRequest{}, map[string]int{"issues": 8}},
39- {&ermite.Event{}, map[string]int{"issue_opened": 17, "issue_closed": 18, "issue_reopened": 19, "issue_commented": 20}},42+ {&ermite.Event{}, map[string]int{"issue_opened": 17, "issue_closed": 18, "issue_reopened": 19, "issue_commented": 20, "issue_submitted": 21}},
43+ {&ermite.IssueSubmitted{}, map[string]int{"repo": 1, "id": 2}},
44+ {&ermite.PendingIssue{}, map[string]int{"id": 1, "repo": 2, "title": 3, "body": 4, "author": 5, "created_at": 6}},
45+ {&ermite.ListPendingIssuesRequest{}, map[string]int{"repo": 1, "page_size": 2, "page_token": 3}},
46+ {&ermite.ListPendingIssuesResponse{}, map[string]int{"issues": 1, "next_page_token": 2}},
47+ {&ermite.ApproveIssueRequest{}, map[string]int{"repo": 1, "id": 2}},
48+ {&ermite.ApproveIssueResponse{}, map[string]int{"issue": 1}},
49+ {&ermite.RejectIssueRequest{}, map[string]int{"repo": 1, "id": 2, "ban": 3}},
40 } {50 } {
41 md := tt.msg.ProtoReflect().Descriptor()51 md := tt.msg.ProtoReflect().Descriptor()
42 for name, num := range tt.fields {52 for name, num := range tt.fields {
@@ -64,6 +74,9 @@ func TestIssueContract(t *testing.T) {
64 &ermite.Event{Kind: &ermite.Event_IssueOpened{IssueOpened: &ermite.IssueOpened{Issue: &ermite.Issue{Repo: "r", Number: 1}}}},74 &ermite.Event{Kind: &ermite.Event_IssueOpened{IssueOpened: &ermite.IssueOpened{Issue: &ermite.Issue{Repo: "r", Number: 1}}}},
65 &ermite.Event{Kind: &ermite.Event_IssueClosed{IssueClosed: &ermite.IssueClosed{Issue: &ermite.Issue{Repo: "r", Number: 1}}}},75 &ermite.Event{Kind: &ermite.Event_IssueClosed{IssueClosed: &ermite.IssueClosed{Issue: &ermite.Issue{Repo: "r", Number: 1}}}},
66 &ermite.Event{Kind: &ermite.Event_IssueReopened{IssueReopened: &ermite.IssueReopened{Issue: &ermite.Issue{Repo: "r", Number: 1}}}},76 &ermite.Event{Kind: &ermite.Event_IssueReopened{IssueReopened: &ermite.IssueReopened{Issue: &ermite.Issue{Repo: "r", Number: 1}}}},
77+ &ermite.Event{Kind: &ermite.Event_IssueSubmitted{IssueSubmitted: &ermite.IssueSubmitted{Repo: "r", Id: "p1"}}},
78+ &ermite.PendingIssue{Id: "p1", Repo: "r", Title: "t", Author: "anon"},
79+ &ermite.RejectIssueRequest{Repo: "r", Id: "p1", Ban: true},
67 &ermite.Event{Kind: &ermite.Event_IssueCommented{IssueCommented: &ermite.IssueCommented{Repo: "r", Number: 1, Comment: &ermite.IssueComment{Id: "c", Text: "hi"}}}},80 &ermite.Event{Kind: &ermite.Event_IssueCommented{IssueCommented: &ermite.IssueCommented{Repo: "r", Number: 1, Comment: &ermite.IssueComment{Id: "c", Text: "hi"}}}},
68 } {81 } {
69 b, err := proto.Marshal(in)82 b, err := proto.Marshal(in)
M proto/ermite/event.proto +15 -1
@@ -20,7 +20,8 @@ service EventService {
2020 // for merge requests, merge_request_opened, merge_request_merged,
2121 // merge_request_closed, comment_added, comment_edited, comment_deleted and
2222 // comment_resolved (a thread resolved or reopened); for issues, issue_opened,
23- // issue_closed, issue_reopened and issue_commented. Each one is delivered on the
23+ // issue_closed, issue_reopened and issue_commented (and issue_submitted, to
24+ // owner watchers only). Each one is delivered on the
2425 // "repo:<name>" topic of its repository and on "home", subject to the
2526 // visibility of the repository.
2627 //
@@ -119,6 +120,9 @@ message Event {
119120 IssueReopened issue_reopened = 19;
120121 // IssueCommented: a comment was added to an issue.
121122 IssueCommented issue_commented = 20;
123+ // IssueSubmitted: an issue is awaiting moderation. Delivered to owner
124+ // watchers only.
125+ IssueSubmitted issue_submitted = 21;
122126 }
123127 }
124128
@@ -247,6 +251,16 @@ message IssueCommented {
247251 IssueComment comment = 3;
248252 }
249253
254+// IssueSubmitted reports an issue awaiting moderation. It is delivered to
255+// owner watchers only. It carries no title or body: content never travels in
256+// events, read it with IssueService.ListPendingIssues.
257+message IssueSubmitted {
258+ // Repo is the name of the repository.
259+ string repo = 1;
260+ // Id is the PendingIssue.id.
261+ string id = 2;
262+}
263+
250264 // Gap tells the client that events were lost: the requested resume point is
251265 // older than the server's buffer. It has no fields. The client should reload
252266 // the state it displays (repositories, refs, merge requests and their
@@ -20,7 +20,8 @@ service EventService {
20 // for merge requests, merge_request_opened, merge_request_merged,20 // for merge requests, merge_request_opened, merge_request_merged,
21 // merge_request_closed, comment_added, comment_edited, comment_deleted and21 // merge_request_closed, comment_added, comment_edited, comment_deleted and
22 // comment_resolved (a thread resolved or reopened); for issues, issue_opened,22 // comment_resolved (a thread resolved or reopened); for issues, issue_opened,
23- // issue_closed, issue_reopened and issue_commented. Each one is delivered on the23+ // issue_closed, issue_reopened and issue_commented (and issue_submitted, to
24+ // owner watchers only). Each one is delivered on the
24 // "repo:<name>" topic of its repository and on "home", subject to the25 // "repo:<name>" topic of its repository and on "home", subject to the
25 // visibility of the repository.26 // visibility of the repository.
26 //27 //
@@ -119,6 +120,9 @@ message Event {
119 IssueReopened issue_reopened = 19;120 IssueReopened issue_reopened = 19;
120 // IssueCommented: a comment was added to an issue.121 // IssueCommented: a comment was added to an issue.
121 IssueCommented issue_commented = 20;122 IssueCommented issue_commented = 20;
123+ // IssueSubmitted: an issue is awaiting moderation. Delivered to owner
124+ // watchers only.
125+ IssueSubmitted issue_submitted = 21;
122 }126 }
123 }127 }
124 128
@@ -247,6 +251,16 @@ message IssueCommented {
247 IssueComment comment = 3;251 IssueComment comment = 3;
248 }252 }
249 253
254+// IssueSubmitted reports an issue awaiting moderation. It is delivered to
255+// owner watchers only. It carries no title or body: content never travels in
256+// events, read it with IssueService.ListPendingIssues.
257+message IssueSubmitted {
258+ // Repo is the name of the repository.
259+ string repo = 1;
260+ // Id is the PendingIssue.id.
261+ string id = 2;
262+}
263+
250 // Gap tells the client that events were lost: the requested resume point is264 // Gap tells the client that events were lost: the requested resume point is
251 // older than the server's buffer. It has no fields. The client should reload265 // older than the server's buffer. It has no fields. The client should reload
252 // the state it displays (repositories, refs, merge requests and their266 // the state it displays (repositories, refs, merge requests and their
M proto/ermite/issue.proto +75 -0
@@ -69,6 +69,15 @@ service IssueService {
6969 rpc CloseIssue(CloseIssueRequest) returns (CloseIssueResponse);
7070 // ReopenIssue reopens an issue. Needs the issue:write scope.
7171 rpc ReopenIssue(ReopenIssueRequest) returns (ReopenIssueResponse);
72+ // ListPendingIssues lists issues awaiting moderation, oldest first. Owner
73+ // rights only; anyone else gets NOT_FOUND.
74+ rpc ListPendingIssues(ListPendingIssuesRequest) returns (ListPendingIssuesResponse);
75+ // ApproveIssue turns a pending issue into an open issue with a new number.
76+ // Owner rights only; anyone else gets NOT_FOUND.
77+ rpc ApproveIssue(ApproveIssueRequest) returns (ApproveIssueResponse);
78+ // RejectIssue discards a pending issue, optionally banning its submitter.
79+ // Owner rights only; anyone else gets NOT_FOUND.
80+ rpc RejectIssue(RejectIssueRequest) returns (RejectIssueResponse);
7281 }
7382
7483 // CreateIssueRequest describes the issue to open.
@@ -170,3 +179,69 @@ message ReopenIssueResponse {
170179 // Issue is the reopened issue.
171180 Issue issue = 1;
172181 }
182+
183+// PendingIssue is an issue awaiting moderation. It has no number yet.
184+message PendingIssue {
185+ // Id identifies the pending issue. Opaque internal id, not a number.
186+ string id = 1;
187+ // Repo is the name of the repository.
188+ string repo = 2;
189+ // Title is the one-line title.
190+ string title = 3;
191+ // Body is the content. It may be empty.
192+ string body = 4;
193+ // Author is the optional display name given by the anonymous submitter. It
194+ // may be empty and is not verified.
195+ string author = 5;
196+ // CreatedAt is when the issue was submitted.
197+ google.protobuf.Timestamp created_at = 6;
198+}
199+
200+// ListPendingIssuesRequest selects a page of pending issues.
201+message ListPendingIssuesRequest {
202+ // Repo is the repository name. Empty means every repository.
203+ string repo = 1;
204+ // PageSize is the maximum number of issues to return. 0 means the server
205+ // default; values above the server maximum are lowered to it.
206+ int32 page_size = 2;
207+ // PageToken is the next_page_token of a previous response, to continue the
208+ // same listing. Empty for the first page. Other fields must be unchanged.
209+ string page_token = 3;
210+}
211+
212+// ListPendingIssuesResponse is one page of pending issues.
213+message ListPendingIssuesResponse {
214+ // Issues are the pending issues of this page, oldest first.
215+ repeated PendingIssue issues = 1;
216+ // NextPageToken is passed as page_token to get the next page. Empty when
217+ // there is no next page.
218+ string next_page_token = 2;
219+}
220+
221+// ApproveIssueRequest names the pending issue to approve.
222+message ApproveIssueRequest {
223+ // Repo is the repository name.
224+ string repo = 1;
225+ // Id is the PendingIssue.id.
226+ string id = 2;
227+}
228+
229+// ApproveIssueResponse holds the approved issue.
230+message ApproveIssueResponse {
231+ // Issue is the approved issue, with its new number and state
232+ // ISSUE_STATE_OPEN.
233+ Issue issue = 1;
234+}
235+
236+// RejectIssueRequest names the pending issue to reject.
237+message RejectIssueRequest {
238+ // Repo is the repository name.
239+ string repo = 1;
240+ // Id is the PendingIssue.id.
241+ string id = 2;
242+ // Ban also bans the submitter for a while.
243+ bool ban = 3;
244+}
245+
246+// RejectIssueResponse is empty: the pending issue is gone.
247+message RejectIssueResponse {}
@@ -69,6 +69,15 @@ service IssueService {
69 rpc CloseIssue(CloseIssueRequest) returns (CloseIssueResponse);69 rpc CloseIssue(CloseIssueRequest) returns (CloseIssueResponse);
70 // ReopenIssue reopens an issue. Needs the issue:write scope.70 // ReopenIssue reopens an issue. Needs the issue:write scope.
71 rpc ReopenIssue(ReopenIssueRequest) returns (ReopenIssueResponse);71 rpc ReopenIssue(ReopenIssueRequest) returns (ReopenIssueResponse);
72+ // ListPendingIssues lists issues awaiting moderation, oldest first. Owner
73+ // rights only; anyone else gets NOT_FOUND.
74+ rpc ListPendingIssues(ListPendingIssuesRequest) returns (ListPendingIssuesResponse);
75+ // ApproveIssue turns a pending issue into an open issue with a new number.
76+ // Owner rights only; anyone else gets NOT_FOUND.
77+ rpc ApproveIssue(ApproveIssueRequest) returns (ApproveIssueResponse);
78+ // RejectIssue discards a pending issue, optionally banning its submitter.
79+ // Owner rights only; anyone else gets NOT_FOUND.
80+ rpc RejectIssue(RejectIssueRequest) returns (RejectIssueResponse);
72 }81 }
73 82
74 // CreateIssueRequest describes the issue to open.83 // CreateIssueRequest describes the issue to open.
@@ -170,3 +179,69 @@ message ReopenIssueResponse {
170 // Issue is the reopened issue.179 // Issue is the reopened issue.
171 Issue issue = 1;180 Issue issue = 1;
172 }181 }
182+
183+// PendingIssue is an issue awaiting moderation. It has no number yet.
184+message PendingIssue {
185+ // Id identifies the pending issue. Opaque internal id, not a number.
186+ string id = 1;
187+ // Repo is the name of the repository.
188+ string repo = 2;
189+ // Title is the one-line title.
190+ string title = 3;
191+ // Body is the content. It may be empty.
192+ string body = 4;
193+ // Author is the optional display name given by the anonymous submitter. It
194+ // may be empty and is not verified.
195+ string author = 5;
196+ // CreatedAt is when the issue was submitted.
197+ google.protobuf.Timestamp created_at = 6;
198+}
199+
200+// ListPendingIssuesRequest selects a page of pending issues.
201+message ListPendingIssuesRequest {
202+ // Repo is the repository name. Empty means every repository.
203+ string repo = 1;
204+ // PageSize is the maximum number of issues to return. 0 means the server
205+ // default; values above the server maximum are lowered to it.
206+ int32 page_size = 2;
207+ // PageToken is the next_page_token of a previous response, to continue the
208+ // same listing. Empty for the first page. Other fields must be unchanged.
209+ string page_token = 3;
210+}
211+
212+// ListPendingIssuesResponse is one page of pending issues.
213+message ListPendingIssuesResponse {
214+ // Issues are the pending issues of this page, oldest first.
215+ repeated PendingIssue issues = 1;
216+ // NextPageToken is passed as page_token to get the next page. Empty when
217+ // there is no next page.
218+ string next_page_token = 2;
219+}
220+
221+// ApproveIssueRequest names the pending issue to approve.
222+message ApproveIssueRequest {
223+ // Repo is the repository name.
224+ string repo = 1;
225+ // Id is the PendingIssue.id.
226+ string id = 2;
227+}
228+
229+// ApproveIssueResponse holds the approved issue.
230+message ApproveIssueResponse {
231+ // Issue is the approved issue, with its new number and state
232+ // ISSUE_STATE_OPEN.
233+ Issue issue = 1;
234+}
235+
236+// RejectIssueRequest names the pending issue to reject.
237+message RejectIssueRequest {
238+ // Repo is the repository name.
239+ string repo = 1;
240+ // Id is the PendingIssue.id.
241+ string id = 2;
242+ // Ban also bans the submitter for a while.
243+ bool ban = 3;
244+}
245+
246+// RejectIssueResponse is empty: the pending issue is gone.
247+message RejectIssueResponse {}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 2

Tags 18

↑↓ moveenter openesc close