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 generated file: collapsed
M gen/ermite/issue.pb.go +503 -41
@@ -939,6 +939,423 @@ func (x *ReopenIssueResponse) GetIssue() *Issue {
939939 return nil
940940 }
941941
942+// PendingIssue is an issue awaiting moderation. It has no number yet.
943+type PendingIssue struct {
944+ state protoimpl.MessageState `protogen:"open.v1"`
945+ // Id identifies the pending issue. Opaque internal id, not a number.
946+ Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
947+ // Repo is the name of the repository.
948+ Repo string `protobuf:"bytes,2,opt,name=repo,proto3" json:"repo,omitempty"`
949+ // Title is the one-line title.
950+ Title string `protobuf:"bytes,3,opt,name=title,proto3" json:"title,omitempty"`
951+ // Body is the content. It may be empty.
952+ Body string `protobuf:"bytes,4,opt,name=body,proto3" json:"body,omitempty"`
953+ // Author is the optional display name given by the anonymous submitter. It
954+ // may be empty and is not verified.
955+ Author string `protobuf:"bytes,5,opt,name=author,proto3" json:"author,omitempty"`
956+ // CreatedAt is when the issue was submitted.
957+ CreatedAt *timestamppb.Timestamp `protobuf:"bytes,6,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"`
958+ unknownFields protoimpl.UnknownFields
959+ sizeCache protoimpl.SizeCache
960+}
961+
962+func (x *PendingIssue) Reset() {
963+ *x = PendingIssue{}
964+ mi := &file_ermite_issue_proto_msgTypes[14]
965+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
966+ ms.StoreMessageInfo(mi)
967+}
968+
969+func (x *PendingIssue) String() string {
970+ return protoimpl.X.MessageStringOf(x)
971+}
972+
973+func (*PendingIssue) ProtoMessage() {}
974+
975+func (x *PendingIssue) ProtoReflect() protoreflect.Message {
976+ mi := &file_ermite_issue_proto_msgTypes[14]
977+ if x != nil {
978+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
979+ if ms.LoadMessageInfo() == nil {
980+ ms.StoreMessageInfo(mi)
981+ }
982+ return ms
983+ }
984+ return mi.MessageOf(x)
985+}
986+
987+// Deprecated: Use PendingIssue.ProtoReflect.Descriptor instead.
988+func (*PendingIssue) Descriptor() ([]byte, []int) {
989+ return file_ermite_issue_proto_rawDescGZIP(), []int{14}
990+}
991+
992+func (x *PendingIssue) GetId() string {
993+ if x != nil {
994+ return x.Id
995+ }
996+ return ""
997+}
998+
999+func (x *PendingIssue) GetRepo() string {
1000+ if x != nil {
1001+ return x.Repo
1002+ }
1003+ return ""
1004+}
1005+
1006+func (x *PendingIssue) GetTitle() string {
1007+ if x != nil {
1008+ return x.Title
1009+ }
1010+ return ""
1011+}
1012+
1013+func (x *PendingIssue) GetBody() string {
1014+ if x != nil {
1015+ return x.Body
1016+ }
1017+ return ""
1018+}
1019+
1020+func (x *PendingIssue) GetAuthor() string {
1021+ if x != nil {
1022+ return x.Author
1023+ }
1024+ return ""
1025+}
1026+
1027+func (x *PendingIssue) GetCreatedAt() *timestamppb.Timestamp {
1028+ if x != nil {
1029+ return x.CreatedAt
1030+ }
1031+ return nil
1032+}
1033+
1034+// ListPendingIssuesRequest selects a page of pending issues.
1035+type ListPendingIssuesRequest struct {
1036+ state protoimpl.MessageState `protogen:"open.v1"`
1037+ // Repo is the repository name. Empty means every repository.
1038+ Repo string `protobuf:"bytes,1,opt,name=repo,proto3" json:"repo,omitempty"`
1039+ // PageSize is the maximum number of issues to return. 0 means the server
1040+ // default; values above the server maximum are lowered to it.
1041+ PageSize int32 `protobuf:"varint,2,opt,name=page_size,json=pageSize,proto3" json:"page_size,omitempty"`
1042+ // PageToken is the next_page_token of a previous response, to continue the
1043+ // same listing. Empty for the first page. Other fields must be unchanged.
1044+ PageToken string `protobuf:"bytes,3,opt,name=page_token,json=pageToken,proto3" json:"page_token,omitempty"`
1045+ unknownFields protoimpl.UnknownFields
1046+ sizeCache protoimpl.SizeCache
1047+}
1048+
1049+func (x *ListPendingIssuesRequest) Reset() {
1050+ *x = ListPendingIssuesRequest{}
1051+ mi := &file_ermite_issue_proto_msgTypes[15]
1052+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
1053+ ms.StoreMessageInfo(mi)
1054+}
1055+
1056+func (x *ListPendingIssuesRequest) String() string {
1057+ return protoimpl.X.MessageStringOf(x)
1058+}
1059+
1060+func (*ListPendingIssuesRequest) ProtoMessage() {}
1061+
1062+func (x *ListPendingIssuesRequest) ProtoReflect() protoreflect.Message {
1063+ mi := &file_ermite_issue_proto_msgTypes[15]
1064+ if x != nil {
1065+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
1066+ if ms.LoadMessageInfo() == nil {
1067+ ms.StoreMessageInfo(mi)
1068+ }
1069+ return ms
1070+ }
1071+ return mi.MessageOf(x)
1072+}
1073+
1074+// Deprecated: Use ListPendingIssuesRequest.ProtoReflect.Descriptor instead.
1075+func (*ListPendingIssuesRequest) Descriptor() ([]byte, []int) {
1076+ return file_ermite_issue_proto_rawDescGZIP(), []int{15}
1077+}
1078+
1079+func (x *ListPendingIssuesRequest) GetRepo() string {
1080+ if x != nil {
1081+ return x.Repo
1082+ }
1083+ return ""
1084+}
1085+
1086+func (x *ListPendingIssuesRequest) GetPageSize() int32 {
1087+ if x != nil {
1088+ return x.PageSize
1089+ }
1090+ return 0
1091+}
1092+
1093+func (x *ListPendingIssuesRequest) GetPageToken() string {
1094+ if x != nil {
1095+ return x.PageToken
1096+ }
1097+ return ""
1098+}
1099+
1100+// ListPendingIssuesResponse is one page of pending issues.
1101+type ListPendingIssuesResponse struct {
1102+ state protoimpl.MessageState `protogen:"open.v1"`
1103+ // Issues are the pending issues of this page, oldest first.
1104+ Issues []*PendingIssue `protobuf:"bytes,1,rep,name=issues,proto3" json:"issues,omitempty"`
1105+ // NextPageToken is passed as page_token to get the next page. Empty when
1106+ // there is no next page.
1107+ NextPageToken string `protobuf:"bytes,2,opt,name=next_page_token,json=nextPageToken,proto3" json:"next_page_token,omitempty"`
1108+ unknownFields protoimpl.UnknownFields
1109+ sizeCache protoimpl.SizeCache
1110+}
1111+
1112+func (x *ListPendingIssuesResponse) Reset() {
1113+ *x = ListPendingIssuesResponse{}
1114+ mi := &file_ermite_issue_proto_msgTypes[16]
1115+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
1116+ ms.StoreMessageInfo(mi)
1117+}
1118+
1119+func (x *ListPendingIssuesResponse) String() string {
1120+ return protoimpl.X.MessageStringOf(x)
1121+}
1122+
1123+func (*ListPendingIssuesResponse) ProtoMessage() {}
1124+
1125+func (x *ListPendingIssuesResponse) ProtoReflect() protoreflect.Message {
1126+ mi := &file_ermite_issue_proto_msgTypes[16]
1127+ if x != nil {
1128+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
1129+ if ms.LoadMessageInfo() == nil {
1130+ ms.StoreMessageInfo(mi)
1131+ }
1132+ return ms
1133+ }
1134+ return mi.MessageOf(x)
1135+}
1136+
1137+// Deprecated: Use ListPendingIssuesResponse.ProtoReflect.Descriptor instead.
1138+func (*ListPendingIssuesResponse) Descriptor() ([]byte, []int) {
1139+ return file_ermite_issue_proto_rawDescGZIP(), []int{16}
1140+}
1141+
1142+func (x *ListPendingIssuesResponse) GetIssues() []*PendingIssue {
1143+ if x != nil {
1144+ return x.Issues
1145+ }
1146+ return nil
1147+}
1148+
1149+func (x *ListPendingIssuesResponse) GetNextPageToken() string {
1150+ if x != nil {
1151+ return x.NextPageToken
1152+ }
1153+ return ""
1154+}
1155+
1156+// ApproveIssueRequest names the pending issue to approve.
1157+type ApproveIssueRequest struct {
1158+ state protoimpl.MessageState `protogen:"open.v1"`
1159+ // Repo is the repository name.
1160+ Repo string `protobuf:"bytes,1,opt,name=repo,proto3" json:"repo,omitempty"`
1161+ // Id is the PendingIssue.id.
1162+ Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"`
1163+ unknownFields protoimpl.UnknownFields
1164+ sizeCache protoimpl.SizeCache
1165+}
1166+
1167+func (x *ApproveIssueRequest) Reset() {
1168+ *x = ApproveIssueRequest{}
1169+ mi := &file_ermite_issue_proto_msgTypes[17]
1170+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
1171+ ms.StoreMessageInfo(mi)
1172+}
1173+
1174+func (x *ApproveIssueRequest) String() string {
1175+ return protoimpl.X.MessageStringOf(x)
1176+}
1177+
1178+func (*ApproveIssueRequest) ProtoMessage() {}
1179+
1180+func (x *ApproveIssueRequest) ProtoReflect() protoreflect.Message {
1181+ mi := &file_ermite_issue_proto_msgTypes[17]
1182+ if x != nil {
1183+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
1184+ if ms.LoadMessageInfo() == nil {
1185+ ms.StoreMessageInfo(mi)
1186+ }
1187+ return ms
1188+ }
1189+ return mi.MessageOf(x)
1190+}
1191+
1192+// Deprecated: Use ApproveIssueRequest.ProtoReflect.Descriptor instead.
1193+func (*ApproveIssueRequest) Descriptor() ([]byte, []int) {
1194+ return file_ermite_issue_proto_rawDescGZIP(), []int{17}
1195+}
1196+
1197+func (x *ApproveIssueRequest) GetRepo() string {
1198+ if x != nil {
1199+ return x.Repo
1200+ }
1201+ return ""
1202+}
1203+
1204+func (x *ApproveIssueRequest) GetId() string {
1205+ if x != nil {
1206+ return x.Id
1207+ }
1208+ return ""
1209+}
1210+
1211+// ApproveIssueResponse holds the approved issue.
1212+type ApproveIssueResponse struct {
1213+ state protoimpl.MessageState `protogen:"open.v1"`
1214+ // Issue is the approved issue, with its new number and state
1215+ // ISSUE_STATE_OPEN.
1216+ Issue *Issue `protobuf:"bytes,1,opt,name=issue,proto3" json:"issue,omitempty"`
1217+ unknownFields protoimpl.UnknownFields
1218+ sizeCache protoimpl.SizeCache
1219+}
1220+
1221+func (x *ApproveIssueResponse) Reset() {
1222+ *x = ApproveIssueResponse{}
1223+ mi := &file_ermite_issue_proto_msgTypes[18]
1224+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
1225+ ms.StoreMessageInfo(mi)
1226+}
1227+
1228+func (x *ApproveIssueResponse) String() string {
1229+ return protoimpl.X.MessageStringOf(x)
1230+}
1231+
1232+func (*ApproveIssueResponse) ProtoMessage() {}
1233+
1234+func (x *ApproveIssueResponse) ProtoReflect() protoreflect.Message {
1235+ mi := &file_ermite_issue_proto_msgTypes[18]
1236+ if x != nil {
1237+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
1238+ if ms.LoadMessageInfo() == nil {
1239+ ms.StoreMessageInfo(mi)
1240+ }
1241+ return ms
1242+ }
1243+ return mi.MessageOf(x)
1244+}
1245+
1246+// Deprecated: Use ApproveIssueResponse.ProtoReflect.Descriptor instead.
1247+func (*ApproveIssueResponse) Descriptor() ([]byte, []int) {
1248+ return file_ermite_issue_proto_rawDescGZIP(), []int{18}
1249+}
1250+
1251+func (x *ApproveIssueResponse) GetIssue() *Issue {
1252+ if x != nil {
1253+ return x.Issue
1254+ }
1255+ return nil
1256+}
1257+
1258+// RejectIssueRequest names the pending issue to reject.
1259+type RejectIssueRequest struct {
1260+ state protoimpl.MessageState `protogen:"open.v1"`
1261+ // Repo is the repository name.
1262+ Repo string `protobuf:"bytes,1,opt,name=repo,proto3" json:"repo,omitempty"`
1263+ // Id is the PendingIssue.id.
1264+ Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"`
1265+ // Ban also bans the submitter for a while.
1266+ Ban bool `protobuf:"varint,3,opt,name=ban,proto3" json:"ban,omitempty"`
1267+ unknownFields protoimpl.UnknownFields
1268+ sizeCache protoimpl.SizeCache
1269+}
1270+
1271+func (x *RejectIssueRequest) Reset() {
1272+ *x = RejectIssueRequest{}
1273+ mi := &file_ermite_issue_proto_msgTypes[19]
1274+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
1275+ ms.StoreMessageInfo(mi)
1276+}
1277+
1278+func (x *RejectIssueRequest) String() string {
1279+ return protoimpl.X.MessageStringOf(x)
1280+}
1281+
1282+func (*RejectIssueRequest) ProtoMessage() {}
1283+
1284+func (x *RejectIssueRequest) ProtoReflect() protoreflect.Message {
1285+ mi := &file_ermite_issue_proto_msgTypes[19]
1286+ if x != nil {
1287+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
1288+ if ms.LoadMessageInfo() == nil {
1289+ ms.StoreMessageInfo(mi)
1290+ }
1291+ return ms
1292+ }
1293+ return mi.MessageOf(x)
1294+}
1295+
1296+// Deprecated: Use RejectIssueRequest.ProtoReflect.Descriptor instead.
1297+func (*RejectIssueRequest) Descriptor() ([]byte, []int) {
1298+ return file_ermite_issue_proto_rawDescGZIP(), []int{19}
1299+}
1300+
1301+func (x *RejectIssueRequest) GetRepo() string {
1302+ if x != nil {
1303+ return x.Repo
1304+ }
1305+ return ""
1306+}
1307+
1308+func (x *RejectIssueRequest) GetId() string {
1309+ if x != nil {
1310+ return x.Id
1311+ }
1312+ return ""
1313+}
1314+
1315+func (x *RejectIssueRequest) GetBan() bool {
1316+ if x != nil {
1317+ return x.Ban
1318+ }
1319+ return false
1320+}
1321+
1322+// RejectIssueResponse is empty: the pending issue is gone.
1323+type RejectIssueResponse struct {
1324+ state protoimpl.MessageState `protogen:"open.v1"`
1325+ unknownFields protoimpl.UnknownFields
1326+ sizeCache protoimpl.SizeCache
1327+}
1328+
1329+func (x *RejectIssueResponse) Reset() {
1330+ *x = RejectIssueResponse{}
1331+ mi := &file_ermite_issue_proto_msgTypes[20]
1332+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
1333+ ms.StoreMessageInfo(mi)
1334+}
1335+
1336+func (x *RejectIssueResponse) String() string {
1337+ return protoimpl.X.MessageStringOf(x)
1338+}
1339+
1340+func (*RejectIssueResponse) ProtoMessage() {}
1341+
1342+func (x *RejectIssueResponse) ProtoReflect() protoreflect.Message {
1343+ mi := &file_ermite_issue_proto_msgTypes[20]
1344+ if x != nil {
1345+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
1346+ if ms.LoadMessageInfo() == nil {
1347+ ms.StoreMessageInfo(mi)
1348+ }
1349+ return ms
1350+ }
1351+ return mi.MessageOf(x)
1352+}
1353+
1354+// Deprecated: Use RejectIssueResponse.ProtoReflect.Descriptor instead.
1355+func (*RejectIssueResponse) Descriptor() ([]byte, []int) {
1356+ return file_ermite_issue_proto_rawDescGZIP(), []int{20}
1357+}
1358+
9421359 var File_ermite_issue_proto protoreflect.FileDescriptor
9431360
9441361 const file_ermite_issue_proto_rawDesc = "" +
@@ -998,13 +1415,39 @@ const file_ermite_issue_proto_rawDesc = "" +
9981415 "\x04repo\x18\x01 \x01(\tR\x04repo\x12\x16\n" +
9991416 "\x06number\x18\x02 \x01(\x03R\x06number\":\n" +
10001417 "\x13ReopenIssueResponse\x12#\n" +
1001- "\x05issue\x18\x01 \x01(\v2\r.ermite.IssueR\x05issue*p\n" +
1418+ "\x05issue\x18\x01 \x01(\v2\r.ermite.IssueR\x05issue\"\xaf\x01\n" +
1419+ "\fPendingIssue\x12\x0e\n" +
1420+ "\x02id\x18\x01 \x01(\tR\x02id\x12\x12\n" +
1421+ "\x04repo\x18\x02 \x01(\tR\x04repo\x12\x14\n" +
1422+ "\x05title\x18\x03 \x01(\tR\x05title\x12\x12\n" +
1423+ "\x04body\x18\x04 \x01(\tR\x04body\x12\x16\n" +
1424+ "\x06author\x18\x05 \x01(\tR\x06author\x129\n" +
1425+ "\n" +
1426+ "created_at\x18\x06 \x01(\v2\x1a.google.protobuf.TimestampR\tcreatedAt\"j\n" +
1427+ "\x18ListPendingIssuesRequest\x12\x12\n" +
1428+ "\x04repo\x18\x01 \x01(\tR\x04repo\x12\x1b\n" +
1429+ "\tpage_size\x18\x02 \x01(\x05R\bpageSize\x12\x1d\n" +
1430+ "\n" +
1431+ "page_token\x18\x03 \x01(\tR\tpageToken\"q\n" +
1432+ "\x19ListPendingIssuesResponse\x12,\n" +
1433+ "\x06issues\x18\x01 \x03(\v2\x14.ermite.PendingIssueR\x06issues\x12&\n" +
1434+ "\x0fnext_page_token\x18\x02 \x01(\tR\rnextPageToken\"9\n" +
1435+ "\x13ApproveIssueRequest\x12\x12\n" +
1436+ "\x04repo\x18\x01 \x01(\tR\x04repo\x12\x0e\n" +
1437+ "\x02id\x18\x02 \x01(\tR\x02id\";\n" +
1438+ "\x14ApproveIssueResponse\x12#\n" +
1439+ "\x05issue\x18\x01 \x01(\v2\r.ermite.IssueR\x05issue\"J\n" +
1440+ "\x12RejectIssueRequest\x12\x12\n" +
1441+ "\x04repo\x18\x01 \x01(\tR\x04repo\x12\x0e\n" +
1442+ "\x02id\x18\x02 \x01(\tR\x02id\x12\x10\n" +
1443+ "\x03ban\x18\x03 \x01(\bR\x03ban\"\x15\n" +
1444+ "\x13RejectIssueResponse*p\n" +
10021445 "\n" +
10031446 "IssueState\x12\x1b\n" +
10041447 "\x17ISSUE_STATE_UNSPECIFIED\x10\x00\x12\x14\n" +
10051448 "\x10ISSUE_STATE_OPEN\x10\x01\x12\x16\n" +
10061449 "\x12ISSUE_STATE_CLOSED\x10\x02\x12\x17\n" +
1007- "\x13ISSUE_STATE_PENDING\x10\x032\xbb\x03\n" +
1450+ "\x13ISSUE_STATE_PENDING\x10\x032\xa8\x05\n" +
10081451 "\fIssueService\x12F\n" +
10091452 "\vCreateIssue\x12\x1a.ermite.CreateIssueRequest\x1a\x1b.ermite.CreateIssueResponse\x12C\n" +
10101453 "\n" +
@@ -1013,7 +1456,10 @@ const file_ermite_issue_proto_rawDesc = "" +
10131456 "\x0fAddIssueComment\x12\x1e.ermite.AddIssueCommentRequest\x1a\x1f.ermite.AddIssueCommentResponse\x12C\n" +
10141457 "\n" +
10151458 "CloseIssue\x12\x19.ermite.CloseIssueRequest\x1a\x1a.ermite.CloseIssueResponse\x12F\n" +
1016- "\vReopenIssue\x12\x1a.ermite.ReopenIssueRequest\x1a\x1b.ermite.ReopenIssueResponseBq\n" +
1459+ "\vReopenIssue\x12\x1a.ermite.ReopenIssueRequest\x1a\x1b.ermite.ReopenIssueResponse\x12X\n" +
1460+ "\x11ListPendingIssues\x12 .ermite.ListPendingIssuesRequest\x1a!.ermite.ListPendingIssuesResponse\x12I\n" +
1461+ "\fApproveIssue\x12\x1b.ermite.ApproveIssueRequest\x1a\x1c.ermite.ApproveIssueResponse\x12F\n" +
1462+ "\vRejectIssue\x12\x1a.ermite.RejectIssueRequest\x1a\x1b.ermite.RejectIssueResponseBq\n" +
10171463 "\n" +
10181464 "com.ermiteB\n" +
10191465 "IssueProtoP\x01Z\x1fbnema.dev/ermite-api/gen/ermite\xa2\x02\x03EXX\xaa\x02\x06Ermite\xca\x02\x06Ermite\xe2\x02\x12Ermite\\GPBMetadata\xea\x02\x06Ermiteb\x06proto3"
@@ -1031,30 +1477,37 @@ func file_ermite_issue_proto_rawDescGZIP() []byte {
10311477 }
10321478
10331479 var file_ermite_issue_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
1034-var file_ermite_issue_proto_msgTypes = make([]protoimpl.MessageInfo, 14)
1480+var file_ermite_issue_proto_msgTypes = make([]protoimpl.MessageInfo, 21)
10351481 var file_ermite_issue_proto_goTypes = []any{
1036- (IssueState)(0), // 0: ermite.IssueState
1037- (*Issue)(nil), // 1: ermite.Issue
1038- (*IssueComment)(nil), // 2: ermite.IssueComment
1039- (*CreateIssueRequest)(nil), // 3: ermite.CreateIssueRequest
1040- (*CreateIssueResponse)(nil), // 4: ermite.CreateIssueResponse
1041- (*ListIssuesRequest)(nil), // 5: ermite.ListIssuesRequest
1042- (*ListIssuesResponse)(nil), // 6: ermite.ListIssuesResponse
1043- (*GetIssueRequest)(nil), // 7: ermite.GetIssueRequest
1044- (*GetIssueResponse)(nil), // 8: ermite.GetIssueResponse
1045- (*AddIssueCommentRequest)(nil), // 9: ermite.AddIssueCommentRequest
1046- (*AddIssueCommentResponse)(nil), // 10: ermite.AddIssueCommentResponse
1047- (*CloseIssueRequest)(nil), // 11: ermite.CloseIssueRequest
1048- (*CloseIssueResponse)(nil), // 12: ermite.CloseIssueResponse
1049- (*ReopenIssueRequest)(nil), // 13: ermite.ReopenIssueRequest
1050- (*ReopenIssueResponse)(nil), // 14: ermite.ReopenIssueResponse
1051- (*timestamppb.Timestamp)(nil), // 15: google.protobuf.Timestamp
1482+ (IssueState)(0), // 0: ermite.IssueState
1483+ (*Issue)(nil), // 1: ermite.Issue
1484+ (*IssueComment)(nil), // 2: ermite.IssueComment
1485+ (*CreateIssueRequest)(nil), // 3: ermite.CreateIssueRequest
1486+ (*CreateIssueResponse)(nil), // 4: ermite.CreateIssueResponse
1487+ (*ListIssuesRequest)(nil), // 5: ermite.ListIssuesRequest
1488+ (*ListIssuesResponse)(nil), // 6: ermite.ListIssuesResponse
1489+ (*GetIssueRequest)(nil), // 7: ermite.GetIssueRequest
1490+ (*GetIssueResponse)(nil), // 8: ermite.GetIssueResponse
1491+ (*AddIssueCommentRequest)(nil), // 9: ermite.AddIssueCommentRequest
1492+ (*AddIssueCommentResponse)(nil), // 10: ermite.AddIssueCommentResponse
1493+ (*CloseIssueRequest)(nil), // 11: ermite.CloseIssueRequest
1494+ (*CloseIssueResponse)(nil), // 12: ermite.CloseIssueResponse
1495+ (*ReopenIssueRequest)(nil), // 13: ermite.ReopenIssueRequest
1496+ (*ReopenIssueResponse)(nil), // 14: ermite.ReopenIssueResponse
1497+ (*PendingIssue)(nil), // 15: ermite.PendingIssue
1498+ (*ListPendingIssuesRequest)(nil), // 16: ermite.ListPendingIssuesRequest
1499+ (*ListPendingIssuesResponse)(nil), // 17: ermite.ListPendingIssuesResponse
1500+ (*ApproveIssueRequest)(nil), // 18: ermite.ApproveIssueRequest
1501+ (*ApproveIssueResponse)(nil), // 19: ermite.ApproveIssueResponse
1502+ (*RejectIssueRequest)(nil), // 20: ermite.RejectIssueRequest
1503+ (*RejectIssueResponse)(nil), // 21: ermite.RejectIssueResponse
1504+ (*timestamppb.Timestamp)(nil), // 22: google.protobuf.Timestamp
10521505 }
10531506 var file_ermite_issue_proto_depIdxs = []int32{
10541507 0, // 0: ermite.Issue.state:type_name -> ermite.IssueState
1055- 15, // 1: ermite.Issue.created_at:type_name -> google.protobuf.Timestamp
1056- 15, // 2: ermite.Issue.updated_at:type_name -> google.protobuf.Timestamp
1057- 15, // 3: ermite.IssueComment.created_at:type_name -> google.protobuf.Timestamp
1508+ 22, // 1: ermite.Issue.created_at:type_name -> google.protobuf.Timestamp
1509+ 22, // 2: ermite.Issue.updated_at:type_name -> google.protobuf.Timestamp
1510+ 22, // 3: ermite.IssueComment.created_at:type_name -> google.protobuf.Timestamp
10581511 1, // 4: ermite.CreateIssueResponse.issue:type_name -> ermite.Issue
10591512 0, // 5: ermite.ListIssuesRequest.state:type_name -> ermite.IssueState
10601513 1, // 6: ermite.ListIssuesResponse.issues:type_name -> ermite.Issue
@@ -1063,23 +1516,32 @@ var file_ermite_issue_proto_depIdxs = []int32{
10631516 2, // 9: ermite.AddIssueCommentResponse.comment:type_name -> ermite.IssueComment
10641517 1, // 10: ermite.CloseIssueResponse.issue:type_name -> ermite.Issue
10651518 1, // 11: ermite.ReopenIssueResponse.issue:type_name -> ermite.Issue
1066- 3, // 12: ermite.IssueService.CreateIssue:input_type -> ermite.CreateIssueRequest
1067- 5, // 13: ermite.IssueService.ListIssues:input_type -> ermite.ListIssuesRequest
1068- 7, // 14: ermite.IssueService.GetIssue:input_type -> ermite.GetIssueRequest
1069- 9, // 15: ermite.IssueService.AddIssueComment:input_type -> ermite.AddIssueCommentRequest
1070- 11, // 16: ermite.IssueService.CloseIssue:input_type -> ermite.CloseIssueRequest
1071- 13, // 17: ermite.IssueService.ReopenIssue:input_type -> ermite.ReopenIssueRequest
1072- 4, // 18: ermite.IssueService.CreateIssue:output_type -> ermite.CreateIssueResponse
1073- 6, // 19: ermite.IssueService.ListIssues:output_type -> ermite.ListIssuesResponse
1074- 8, // 20: ermite.IssueService.GetIssue:output_type -> ermite.GetIssueResponse
1075- 10, // 21: ermite.IssueService.AddIssueComment:output_type -> ermite.AddIssueCommentResponse
1076- 12, // 22: ermite.IssueService.CloseIssue:output_type -> ermite.CloseIssueResponse
1077- 14, // 23: ermite.IssueService.ReopenIssue:output_type -> ermite.ReopenIssueResponse
1078- 18, // [18:24] is the sub-list for method output_type
1079- 12, // [12:18] is the sub-list for method input_type
1080- 12, // [12:12] is the sub-list for extension type_name
1081- 12, // [12:12] is the sub-list for extension extendee
1082- 0, // [0:12] is the sub-list for field type_name
1519+ 22, // 12: ermite.PendingIssue.created_at:type_name -> google.protobuf.Timestamp
1520+ 15, // 13: ermite.ListPendingIssuesResponse.issues:type_name -> ermite.PendingIssue
1521+ 1, // 14: ermite.ApproveIssueResponse.issue:type_name -> ermite.Issue
1522+ 3, // 15: ermite.IssueService.CreateIssue:input_type -> ermite.CreateIssueRequest
1523+ 5, // 16: ermite.IssueService.ListIssues:input_type -> ermite.ListIssuesRequest
1524+ 7, // 17: ermite.IssueService.GetIssue:input_type -> ermite.GetIssueRequest
1525+ 9, // 18: ermite.IssueService.AddIssueComment:input_type -> ermite.AddIssueCommentRequest
1526+ 11, // 19: ermite.IssueService.CloseIssue:input_type -> ermite.CloseIssueRequest
1527+ 13, // 20: ermite.IssueService.ReopenIssue:input_type -> ermite.ReopenIssueRequest
1528+ 16, // 21: ermite.IssueService.ListPendingIssues:input_type -> ermite.ListPendingIssuesRequest
1529+ 18, // 22: ermite.IssueService.ApproveIssue:input_type -> ermite.ApproveIssueRequest
1530+ 20, // 23: ermite.IssueService.RejectIssue:input_type -> ermite.RejectIssueRequest
1531+ 4, // 24: ermite.IssueService.CreateIssue:output_type -> ermite.CreateIssueResponse
1532+ 6, // 25: ermite.IssueService.ListIssues:output_type -> ermite.ListIssuesResponse
1533+ 8, // 26: ermite.IssueService.GetIssue:output_type -> ermite.GetIssueResponse
1534+ 10, // 27: ermite.IssueService.AddIssueComment:output_type -> ermite.AddIssueCommentResponse
1535+ 12, // 28: ermite.IssueService.CloseIssue:output_type -> ermite.CloseIssueResponse
1536+ 14, // 29: ermite.IssueService.ReopenIssue:output_type -> ermite.ReopenIssueResponse
1537+ 17, // 30: ermite.IssueService.ListPendingIssues:output_type -> ermite.ListPendingIssuesResponse
1538+ 19, // 31: ermite.IssueService.ApproveIssue:output_type -> ermite.ApproveIssueResponse
1539+ 21, // 32: ermite.IssueService.RejectIssue:output_type -> ermite.RejectIssueResponse
1540+ 24, // [24:33] is the sub-list for method output_type
1541+ 15, // [15:24] is the sub-list for method input_type
1542+ 15, // [15:15] is the sub-list for extension type_name
1543+ 15, // [15:15] is the sub-list for extension extendee
1544+ 0, // [0:15] is the sub-list for field type_name
10831545 }
10841546
10851547 func init() { file_ermite_issue_proto_init() }
@@ -1093,7 +1555,7 @@ func file_ermite_issue_proto_init() {
10931555 GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
10941556 RawDescriptor: unsafe.Slice(unsafe.StringData(file_ermite_issue_proto_rawDesc), len(file_ermite_issue_proto_rawDesc)),
10951557 NumEnums: 1,
1096- NumMessages: 14,
1558+ NumMessages: 21,
10971559 NumExtensions: 0,
10981560 NumServices: 1,
10991561 },
@@ -939,6 +939,423 @@ func (x *ReopenIssueResponse) GetIssue() *Issue {
939 return nil939 return nil
940 }940 }
941 941
942+// PendingIssue is an issue awaiting moderation. It has no number yet.
943+type PendingIssue struct {
944+ state protoimpl.MessageState `protogen:"open.v1"`
945+ // Id identifies the pending issue. Opaque internal id, not a number.
946+ Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
947+ // Repo is the name of the repository.
948+ Repo string `protobuf:"bytes,2,opt,name=repo,proto3" json:"repo,omitempty"`
949+ // Title is the one-line title.
950+ Title string `protobuf:"bytes,3,opt,name=title,proto3" json:"title,omitempty"`
951+ // Body is the content. It may be empty.
952+ Body string `protobuf:"bytes,4,opt,name=body,proto3" json:"body,omitempty"`
953+ // Author is the optional display name given by the anonymous submitter. It
954+ // may be empty and is not verified.
955+ Author string `protobuf:"bytes,5,opt,name=author,proto3" json:"author,omitempty"`
956+ // CreatedAt is when the issue was submitted.
957+ CreatedAt *timestamppb.Timestamp `protobuf:"bytes,6,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"`
958+ unknownFields protoimpl.UnknownFields
959+ sizeCache protoimpl.SizeCache
960+}
961+
962+func (x *PendingIssue) Reset() {
963+ *x = PendingIssue{}
964+ mi := &file_ermite_issue_proto_msgTypes[14]
965+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
966+ ms.StoreMessageInfo(mi)
967+}
968+
969+func (x *PendingIssue) String() string {
970+ return protoimpl.X.MessageStringOf(x)
971+}
972+
973+func (*PendingIssue) ProtoMessage() {}
974+
975+func (x *PendingIssue) ProtoReflect() protoreflect.Message {
976+ mi := &file_ermite_issue_proto_msgTypes[14]
977+ if x != nil {
978+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
979+ if ms.LoadMessageInfo() == nil {
980+ ms.StoreMessageInfo(mi)
981+ }
982+ return ms
983+ }
984+ return mi.MessageOf(x)
985+}
986+
987+// Deprecated: Use PendingIssue.ProtoReflect.Descriptor instead.
988+func (*PendingIssue) Descriptor() ([]byte, []int) {
989+ return file_ermite_issue_proto_rawDescGZIP(), []int{14}
990+}
991+
992+func (x *PendingIssue) GetId() string {
993+ if x != nil {
994+ return x.Id
995+ }
996+ return ""
997+}
998+
999+func (x *PendingIssue) GetRepo() string {
1000+ if x != nil {
1001+ return x.Repo
1002+ }
1003+ return ""
1004+}
1005+
1006+func (x *PendingIssue) GetTitle() string {
1007+ if x != nil {
1008+ return x.Title
1009+ }
1010+ return ""
1011+}
1012+
1013+func (x *PendingIssue) GetBody() string {
1014+ if x != nil {
1015+ return x.Body
1016+ }
1017+ return ""
1018+}
1019+
1020+func (x *PendingIssue) GetAuthor() string {
1021+ if x != nil {
1022+ return x.Author
1023+ }
1024+ return ""
1025+}
1026+
1027+func (x *PendingIssue) GetCreatedAt() *timestamppb.Timestamp {
1028+ if x != nil {
1029+ return x.CreatedAt
1030+ }
1031+ return nil
1032+}
1033+
1034+// ListPendingIssuesRequest selects a page of pending issues.
1035+type ListPendingIssuesRequest struct {
1036+ state protoimpl.MessageState `protogen:"open.v1"`
1037+ // Repo is the repository name. Empty means every repository.
1038+ Repo string `protobuf:"bytes,1,opt,name=repo,proto3" json:"repo,omitempty"`
1039+ // PageSize is the maximum number of issues to return. 0 means the server
1040+ // default; values above the server maximum are lowered to it.
1041+ PageSize int32 `protobuf:"varint,2,opt,name=page_size,json=pageSize,proto3" json:"page_size,omitempty"`
1042+ // PageToken is the next_page_token of a previous response, to continue the
1043+ // same listing. Empty for the first page. Other fields must be unchanged.
1044+ PageToken string `protobuf:"bytes,3,opt,name=page_token,json=pageToken,proto3" json:"page_token,omitempty"`
1045+ unknownFields protoimpl.UnknownFields
1046+ sizeCache protoimpl.SizeCache
1047+}
1048+
1049+func (x *ListPendingIssuesRequest) Reset() {
1050+ *x = ListPendingIssuesRequest{}
1051+ mi := &file_ermite_issue_proto_msgTypes[15]
1052+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
1053+ ms.StoreMessageInfo(mi)
1054+}
1055+
1056+func (x *ListPendingIssuesRequest) String() string {
1057+ return protoimpl.X.MessageStringOf(x)
1058+}
1059+
1060+func (*ListPendingIssuesRequest) ProtoMessage() {}
1061+
1062+func (x *ListPendingIssuesRequest) ProtoReflect() protoreflect.Message {
1063+ mi := &file_ermite_issue_proto_msgTypes[15]
1064+ if x != nil {
1065+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
1066+ if ms.LoadMessageInfo() == nil {
1067+ ms.StoreMessageInfo(mi)
1068+ }
1069+ return ms
1070+ }
1071+ return mi.MessageOf(x)
1072+}
1073+
1074+// Deprecated: Use ListPendingIssuesRequest.ProtoReflect.Descriptor instead.
1075+func (*ListPendingIssuesRequest) Descriptor() ([]byte, []int) {
1076+ return file_ermite_issue_proto_rawDescGZIP(), []int{15}
1077+}
1078+
1079+func (x *ListPendingIssuesRequest) GetRepo() string {
1080+ if x != nil {
1081+ return x.Repo
1082+ }
1083+ return ""
1084+}
1085+
1086+func (x *ListPendingIssuesRequest) GetPageSize() int32 {
1087+ if x != nil {
1088+ return x.PageSize
1089+ }
1090+ return 0
1091+}
1092+
1093+func (x *ListPendingIssuesRequest) GetPageToken() string {
1094+ if x != nil {
1095+ return x.PageToken
1096+ }
1097+ return ""
1098+}
1099+
1100+// ListPendingIssuesResponse is one page of pending issues.
1101+type ListPendingIssuesResponse struct {
1102+ state protoimpl.MessageState `protogen:"open.v1"`
1103+ // Issues are the pending issues of this page, oldest first.
1104+ Issues []*PendingIssue `protobuf:"bytes,1,rep,name=issues,proto3" json:"issues,omitempty"`
1105+ // NextPageToken is passed as page_token to get the next page. Empty when
1106+ // there is no next page.
1107+ NextPageToken string `protobuf:"bytes,2,opt,name=next_page_token,json=nextPageToken,proto3" json:"next_page_token,omitempty"`
1108+ unknownFields protoimpl.UnknownFields
1109+ sizeCache protoimpl.SizeCache
1110+}
1111+
1112+func (x *ListPendingIssuesResponse) Reset() {
1113+ *x = ListPendingIssuesResponse{}
1114+ mi := &file_ermite_issue_proto_msgTypes[16]
1115+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
1116+ ms.StoreMessageInfo(mi)
1117+}
1118+
1119+func (x *ListPendingIssuesResponse) String() string {
1120+ return protoimpl.X.MessageStringOf(x)
1121+}
1122+
1123+func (*ListPendingIssuesResponse) ProtoMessage() {}
1124+
1125+func (x *ListPendingIssuesResponse) ProtoReflect() protoreflect.Message {
1126+ mi := &file_ermite_issue_proto_msgTypes[16]
1127+ if x != nil {
1128+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
1129+ if ms.LoadMessageInfo() == nil {
1130+ ms.StoreMessageInfo(mi)
1131+ }
1132+ return ms
1133+ }
1134+ return mi.MessageOf(x)
1135+}
1136+
1137+// Deprecated: Use ListPendingIssuesResponse.ProtoReflect.Descriptor instead.
1138+func (*ListPendingIssuesResponse) Descriptor() ([]byte, []int) {
1139+ return file_ermite_issue_proto_rawDescGZIP(), []int{16}
1140+}
1141+
1142+func (x *ListPendingIssuesResponse) GetIssues() []*PendingIssue {
1143+ if x != nil {
1144+ return x.Issues
1145+ }
1146+ return nil
1147+}
1148+
1149+func (x *ListPendingIssuesResponse) GetNextPageToken() string {
1150+ if x != nil {
1151+ return x.NextPageToken
1152+ }
1153+ return ""
1154+}
1155+
1156+// ApproveIssueRequest names the pending issue to approve.
1157+type ApproveIssueRequest struct {
1158+ state protoimpl.MessageState `protogen:"open.v1"`
1159+ // Repo is the repository name.
1160+ Repo string `protobuf:"bytes,1,opt,name=repo,proto3" json:"repo,omitempty"`
1161+ // Id is the PendingIssue.id.
1162+ Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"`
1163+ unknownFields protoimpl.UnknownFields
1164+ sizeCache protoimpl.SizeCache
1165+}
1166+
1167+func (x *ApproveIssueRequest) Reset() {
1168+ *x = ApproveIssueRequest{}
1169+ mi := &file_ermite_issue_proto_msgTypes[17]
1170+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
1171+ ms.StoreMessageInfo(mi)
1172+}
1173+
1174+func (x *ApproveIssueRequest) String() string {
1175+ return protoimpl.X.MessageStringOf(x)
1176+}
1177+
1178+func (*ApproveIssueRequest) ProtoMessage() {}
1179+
1180+func (x *ApproveIssueRequest) ProtoReflect() protoreflect.Message {
1181+ mi := &file_ermite_issue_proto_msgTypes[17]
1182+ if x != nil {
1183+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
1184+ if ms.LoadMessageInfo() == nil {
1185+ ms.StoreMessageInfo(mi)
1186+ }
1187+ return ms
1188+ }
1189+ return mi.MessageOf(x)
1190+}
1191+
1192+// Deprecated: Use ApproveIssueRequest.ProtoReflect.Descriptor instead.
1193+func (*ApproveIssueRequest) Descriptor() ([]byte, []int) {
1194+ return file_ermite_issue_proto_rawDescGZIP(), []int{17}
1195+}
1196+
1197+func (x *ApproveIssueRequest) GetRepo() string {
1198+ if x != nil {
1199+ return x.Repo
1200+ }
1201+ return ""
1202+}
1203+
1204+func (x *ApproveIssueRequest) GetId() string {
1205+ if x != nil {
1206+ return x.Id
1207+ }
1208+ return ""
1209+}
1210+
1211+// ApproveIssueResponse holds the approved issue.
1212+type ApproveIssueResponse struct {
1213+ state protoimpl.MessageState `protogen:"open.v1"`
1214+ // Issue is the approved issue, with its new number and state
1215+ // ISSUE_STATE_OPEN.
1216+ Issue *Issue `protobuf:"bytes,1,opt,name=issue,proto3" json:"issue,omitempty"`
1217+ unknownFields protoimpl.UnknownFields
1218+ sizeCache protoimpl.SizeCache
1219+}
1220+
1221+func (x *ApproveIssueResponse) Reset() {
1222+ *x = ApproveIssueResponse{}
1223+ mi := &file_ermite_issue_proto_msgTypes[18]
1224+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
1225+ ms.StoreMessageInfo(mi)
1226+}
1227+
1228+func (x *ApproveIssueResponse) String() string {
1229+ return protoimpl.X.MessageStringOf(x)
1230+}
1231+
1232+func (*ApproveIssueResponse) ProtoMessage() {}
1233+
1234+func (x *ApproveIssueResponse) ProtoReflect() protoreflect.Message {
1235+ mi := &file_ermite_issue_proto_msgTypes[18]
1236+ if x != nil {
1237+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
1238+ if ms.LoadMessageInfo() == nil {
1239+ ms.StoreMessageInfo(mi)
1240+ }
1241+ return ms
1242+ }
1243+ return mi.MessageOf(x)
1244+}
1245+
1246+// Deprecated: Use ApproveIssueResponse.ProtoReflect.Descriptor instead.
1247+func (*ApproveIssueResponse) Descriptor() ([]byte, []int) {
1248+ return file_ermite_issue_proto_rawDescGZIP(), []int{18}
1249+}
1250+
1251+func (x *ApproveIssueResponse) GetIssue() *Issue {
1252+ if x != nil {
1253+ return x.Issue
1254+ }
1255+ return nil
1256+}
1257+
1258+// RejectIssueRequest names the pending issue to reject.
1259+type RejectIssueRequest struct {
1260+ state protoimpl.MessageState `protogen:"open.v1"`
1261+ // Repo is the repository name.
1262+ Repo string `protobuf:"bytes,1,opt,name=repo,proto3" json:"repo,omitempty"`
1263+ // Id is the PendingIssue.id.
1264+ Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"`
1265+ // Ban also bans the submitter for a while.
1266+ Ban bool `protobuf:"varint,3,opt,name=ban,proto3" json:"ban,omitempty"`
1267+ unknownFields protoimpl.UnknownFields
1268+ sizeCache protoimpl.SizeCache
1269+}
1270+
1271+func (x *RejectIssueRequest) Reset() {
1272+ *x = RejectIssueRequest{}
1273+ mi := &file_ermite_issue_proto_msgTypes[19]
1274+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
1275+ ms.StoreMessageInfo(mi)
1276+}
1277+
1278+func (x *RejectIssueRequest) String() string {
1279+ return protoimpl.X.MessageStringOf(x)
1280+}
1281+
1282+func (*RejectIssueRequest) ProtoMessage() {}
1283+
1284+func (x *RejectIssueRequest) ProtoReflect() protoreflect.Message {
1285+ mi := &file_ermite_issue_proto_msgTypes[19]
1286+ if x != nil {
1287+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
1288+ if ms.LoadMessageInfo() == nil {
1289+ ms.StoreMessageInfo(mi)
1290+ }
1291+ return ms
1292+ }
1293+ return mi.MessageOf(x)
1294+}
1295+
1296+// Deprecated: Use RejectIssueRequest.ProtoReflect.Descriptor instead.
1297+func (*RejectIssueRequest) Descriptor() ([]byte, []int) {
1298+ return file_ermite_issue_proto_rawDescGZIP(), []int{19}
1299+}
1300+
1301+func (x *RejectIssueRequest) GetRepo() string {
1302+ if x != nil {
1303+ return x.Repo
1304+ }
1305+ return ""
1306+}
1307+
1308+func (x *RejectIssueRequest) GetId() string {
1309+ if x != nil {
1310+ return x.Id
1311+ }
1312+ return ""
1313+}
1314+
1315+func (x *RejectIssueRequest) GetBan() bool {
1316+ if x != nil {
1317+ return x.Ban
1318+ }
1319+ return false
1320+}
1321+
1322+// RejectIssueResponse is empty: the pending issue is gone.
1323+type RejectIssueResponse struct {
1324+ state protoimpl.MessageState `protogen:"open.v1"`
1325+ unknownFields protoimpl.UnknownFields
1326+ sizeCache protoimpl.SizeCache
1327+}
1328+
1329+func (x *RejectIssueResponse) Reset() {
1330+ *x = RejectIssueResponse{}
1331+ mi := &file_ermite_issue_proto_msgTypes[20]
1332+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
1333+ ms.StoreMessageInfo(mi)
1334+}
1335+
1336+func (x *RejectIssueResponse) String() string {
1337+ return protoimpl.X.MessageStringOf(x)
1338+}
1339+
1340+func (*RejectIssueResponse) ProtoMessage() {}
1341+
1342+func (x *RejectIssueResponse) ProtoReflect() protoreflect.Message {
1343+ mi := &file_ermite_issue_proto_msgTypes[20]
1344+ if x != nil {
1345+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
1346+ if ms.LoadMessageInfo() == nil {
1347+ ms.StoreMessageInfo(mi)
1348+ }
1349+ return ms
1350+ }
1351+ return mi.MessageOf(x)
1352+}
1353+
1354+// Deprecated: Use RejectIssueResponse.ProtoReflect.Descriptor instead.
1355+func (*RejectIssueResponse) Descriptor() ([]byte, []int) {
1356+ return file_ermite_issue_proto_rawDescGZIP(), []int{20}
1357+}
1358+
942 var File_ermite_issue_proto protoreflect.FileDescriptor1359 var File_ermite_issue_proto protoreflect.FileDescriptor
943 1360
944 const file_ermite_issue_proto_rawDesc = "" +1361 const file_ermite_issue_proto_rawDesc = "" +
@@ -998,13 +1415,39 @@ const file_ermite_issue_proto_rawDesc = "" +
998 "\x04repo\x18\x01 \x01(\tR\x04repo\x12\x16\n" +1415 "\x04repo\x18\x01 \x01(\tR\x04repo\x12\x16\n" +
999 "\x06number\x18\x02 \x01(\x03R\x06number\":\n" +1416 "\x06number\x18\x02 \x01(\x03R\x06number\":\n" +
1000 "\x13ReopenIssueResponse\x12#\n" +1417 "\x13ReopenIssueResponse\x12#\n" +
1001- "\x05issue\x18\x01 \x01(\v2\r.ermite.IssueR\x05issue*p\n" +1418+ "\x05issue\x18\x01 \x01(\v2\r.ermite.IssueR\x05issue\"\xaf\x01\n" +
1419+ "\fPendingIssue\x12\x0e\n" +
1420+ "\x02id\x18\x01 \x01(\tR\x02id\x12\x12\n" +
1421+ "\x04repo\x18\x02 \x01(\tR\x04repo\x12\x14\n" +
1422+ "\x05title\x18\x03 \x01(\tR\x05title\x12\x12\n" +
1423+ "\x04body\x18\x04 \x01(\tR\x04body\x12\x16\n" +
1424+ "\x06author\x18\x05 \x01(\tR\x06author\x129\n" +
1425+ "\n" +
1426+ "created_at\x18\x06 \x01(\v2\x1a.google.protobuf.TimestampR\tcreatedAt\"j\n" +
1427+ "\x18ListPendingIssuesRequest\x12\x12\n" +
1428+ "\x04repo\x18\x01 \x01(\tR\x04repo\x12\x1b\n" +
1429+ "\tpage_size\x18\x02 \x01(\x05R\bpageSize\x12\x1d\n" +
1430+ "\n" +
1431+ "page_token\x18\x03 \x01(\tR\tpageToken\"q\n" +
1432+ "\x19ListPendingIssuesResponse\x12,\n" +
1433+ "\x06issues\x18\x01 \x03(\v2\x14.ermite.PendingIssueR\x06issues\x12&\n" +
1434+ "\x0fnext_page_token\x18\x02 \x01(\tR\rnextPageToken\"9\n" +
1435+ "\x13ApproveIssueRequest\x12\x12\n" +
1436+ "\x04repo\x18\x01 \x01(\tR\x04repo\x12\x0e\n" +
1437+ "\x02id\x18\x02 \x01(\tR\x02id\";\n" +
1438+ "\x14ApproveIssueResponse\x12#\n" +
1439+ "\x05issue\x18\x01 \x01(\v2\r.ermite.IssueR\x05issue\"J\n" +
1440+ "\x12RejectIssueRequest\x12\x12\n" +
1441+ "\x04repo\x18\x01 \x01(\tR\x04repo\x12\x0e\n" +
1442+ "\x02id\x18\x02 \x01(\tR\x02id\x12\x10\n" +
1443+ "\x03ban\x18\x03 \x01(\bR\x03ban\"\x15\n" +
1444+ "\x13RejectIssueResponse*p\n" +
1002 "\n" +1445 "\n" +
1003 "IssueState\x12\x1b\n" +1446 "IssueState\x12\x1b\n" +
1004 "\x17ISSUE_STATE_UNSPECIFIED\x10\x00\x12\x14\n" +1447 "\x17ISSUE_STATE_UNSPECIFIED\x10\x00\x12\x14\n" +
1005 "\x10ISSUE_STATE_OPEN\x10\x01\x12\x16\n" +1448 "\x10ISSUE_STATE_OPEN\x10\x01\x12\x16\n" +
1006 "\x12ISSUE_STATE_CLOSED\x10\x02\x12\x17\n" +1449 "\x12ISSUE_STATE_CLOSED\x10\x02\x12\x17\n" +
1007- "\x13ISSUE_STATE_PENDING\x10\x032\xbb\x03\n" +1450+ "\x13ISSUE_STATE_PENDING\x10\x032\xa8\x05\n" +
1008 "\fIssueService\x12F\n" +1451 "\fIssueService\x12F\n" +
1009 "\vCreateIssue\x12\x1a.ermite.CreateIssueRequest\x1a\x1b.ermite.CreateIssueResponse\x12C\n" +1452 "\vCreateIssue\x12\x1a.ermite.CreateIssueRequest\x1a\x1b.ermite.CreateIssueResponse\x12C\n" +
1010 "\n" +1453 "\n" +
@@ -1013,7 +1456,10 @@ const file_ermite_issue_proto_rawDesc = "" +
1013 "\x0fAddIssueComment\x12\x1e.ermite.AddIssueCommentRequest\x1a\x1f.ermite.AddIssueCommentResponse\x12C\n" +1456 "\x0fAddIssueComment\x12\x1e.ermite.AddIssueCommentRequest\x1a\x1f.ermite.AddIssueCommentResponse\x12C\n" +
1014 "\n" +1457 "\n" +
1015 "CloseIssue\x12\x19.ermite.CloseIssueRequest\x1a\x1a.ermite.CloseIssueResponse\x12F\n" +1458 "CloseIssue\x12\x19.ermite.CloseIssueRequest\x1a\x1a.ermite.CloseIssueResponse\x12F\n" +
1016- "\vReopenIssue\x12\x1a.ermite.ReopenIssueRequest\x1a\x1b.ermite.ReopenIssueResponseBq\n" +1459+ "\vReopenIssue\x12\x1a.ermite.ReopenIssueRequest\x1a\x1b.ermite.ReopenIssueResponse\x12X\n" +
1460+ "\x11ListPendingIssues\x12 .ermite.ListPendingIssuesRequest\x1a!.ermite.ListPendingIssuesResponse\x12I\n" +
1461+ "\fApproveIssue\x12\x1b.ermite.ApproveIssueRequest\x1a\x1c.ermite.ApproveIssueResponse\x12F\n" +
1462+ "\vRejectIssue\x12\x1a.ermite.RejectIssueRequest\x1a\x1b.ermite.RejectIssueResponseBq\n" +
1017 "\n" +1463 "\n" +
1018 "com.ermiteB\n" +1464 "com.ermiteB\n" +
1019 "IssueProtoP\x01Z\x1fbnema.dev/ermite-api/gen/ermite\xa2\x02\x03EXX\xaa\x02\x06Ermite\xca\x02\x06Ermite\xe2\x02\x12Ermite\\GPBMetadata\xea\x02\x06Ermiteb\x06proto3"1465 "IssueProtoP\x01Z\x1fbnema.dev/ermite-api/gen/ermite\xa2\x02\x03EXX\xaa\x02\x06Ermite\xca\x02\x06Ermite\xe2\x02\x12Ermite\\GPBMetadata\xea\x02\x06Ermiteb\x06proto3"
@@ -1031,30 +1477,37 @@ func file_ermite_issue_proto_rawDescGZIP() []byte {
1031 }1477 }
1032 1478
1033 var file_ermite_issue_proto_enumTypes = make([]protoimpl.EnumInfo, 1)1479 var file_ermite_issue_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
1034-var file_ermite_issue_proto_msgTypes = make([]protoimpl.MessageInfo, 14)1480+var file_ermite_issue_proto_msgTypes = make([]protoimpl.MessageInfo, 21)
1035 var file_ermite_issue_proto_goTypes = []any{1481 var file_ermite_issue_proto_goTypes = []any{
1036- (IssueState)(0), // 0: ermite.IssueState1482+ (IssueState)(0), // 0: ermite.IssueState
1037- (*Issue)(nil), // 1: ermite.Issue1483+ (*Issue)(nil), // 1: ermite.Issue
1038- (*IssueComment)(nil), // 2: ermite.IssueComment1484+ (*IssueComment)(nil), // 2: ermite.IssueComment
1039- (*CreateIssueRequest)(nil), // 3: ermite.CreateIssueRequest1485+ (*CreateIssueRequest)(nil), // 3: ermite.CreateIssueRequest
1040- (*CreateIssueResponse)(nil), // 4: ermite.CreateIssueResponse1486+ (*CreateIssueResponse)(nil), // 4: ermite.CreateIssueResponse
1041- (*ListIssuesRequest)(nil), // 5: ermite.ListIssuesRequest1487+ (*ListIssuesRequest)(nil), // 5: ermite.ListIssuesRequest
1042- (*ListIssuesResponse)(nil), // 6: ermite.ListIssuesResponse1488+ (*ListIssuesResponse)(nil), // 6: ermite.ListIssuesResponse
1043- (*GetIssueRequest)(nil), // 7: ermite.GetIssueRequest1489+ (*GetIssueRequest)(nil), // 7: ermite.GetIssueRequest
1044- (*GetIssueResponse)(nil), // 8: ermite.GetIssueResponse1490+ (*GetIssueResponse)(nil), // 8: ermite.GetIssueResponse
1045- (*AddIssueCommentRequest)(nil), // 9: ermite.AddIssueCommentRequest1491+ (*AddIssueCommentRequest)(nil), // 9: ermite.AddIssueCommentRequest
1046- (*AddIssueCommentResponse)(nil), // 10: ermite.AddIssueCommentResponse1492+ (*AddIssueCommentResponse)(nil), // 10: ermite.AddIssueCommentResponse
1047- (*CloseIssueRequest)(nil), // 11: ermite.CloseIssueRequest1493+ (*CloseIssueRequest)(nil), // 11: ermite.CloseIssueRequest
1048- (*CloseIssueResponse)(nil), // 12: ermite.CloseIssueResponse1494+ (*CloseIssueResponse)(nil), // 12: ermite.CloseIssueResponse
1049- (*ReopenIssueRequest)(nil), // 13: ermite.ReopenIssueRequest1495+ (*ReopenIssueRequest)(nil), // 13: ermite.ReopenIssueRequest
1050- (*ReopenIssueResponse)(nil), // 14: ermite.ReopenIssueResponse1496+ (*ReopenIssueResponse)(nil), // 14: ermite.ReopenIssueResponse
1051- (*timestamppb.Timestamp)(nil), // 15: google.protobuf.Timestamp1497+ (*PendingIssue)(nil), // 15: ermite.PendingIssue
1498+ (*ListPendingIssuesRequest)(nil), // 16: ermite.ListPendingIssuesRequest
1499+ (*ListPendingIssuesResponse)(nil), // 17: ermite.ListPendingIssuesResponse
1500+ (*ApproveIssueRequest)(nil), // 18: ermite.ApproveIssueRequest
1501+ (*ApproveIssueResponse)(nil), // 19: ermite.ApproveIssueResponse
1502+ (*RejectIssueRequest)(nil), // 20: ermite.RejectIssueRequest
1503+ (*RejectIssueResponse)(nil), // 21: ermite.RejectIssueResponse
1504+ (*timestamppb.Timestamp)(nil), // 22: google.protobuf.Timestamp
1052 }1505 }
1053 var file_ermite_issue_proto_depIdxs = []int32{1506 var file_ermite_issue_proto_depIdxs = []int32{
1054 0, // 0: ermite.Issue.state:type_name -> ermite.IssueState1507 0, // 0: ermite.Issue.state:type_name -> ermite.IssueState
1055- 15, // 1: ermite.Issue.created_at:type_name -> google.protobuf.Timestamp1508+ 22, // 1: ermite.Issue.created_at:type_name -> google.protobuf.Timestamp
1056- 15, // 2: ermite.Issue.updated_at:type_name -> google.protobuf.Timestamp1509+ 22, // 2: ermite.Issue.updated_at:type_name -> google.protobuf.Timestamp
1057- 15, // 3: ermite.IssueComment.created_at:type_name -> google.protobuf.Timestamp1510+ 22, // 3: ermite.IssueComment.created_at:type_name -> google.protobuf.Timestamp
1058 1, // 4: ermite.CreateIssueResponse.issue:type_name -> ermite.Issue1511 1, // 4: ermite.CreateIssueResponse.issue:type_name -> ermite.Issue
1059 0, // 5: ermite.ListIssuesRequest.state:type_name -> ermite.IssueState1512 0, // 5: ermite.ListIssuesRequest.state:type_name -> ermite.IssueState
1060 1, // 6: ermite.ListIssuesResponse.issues:type_name -> ermite.Issue1513 1, // 6: ermite.ListIssuesResponse.issues:type_name -> ermite.Issue
@@ -1063,23 +1516,32 @@ var file_ermite_issue_proto_depIdxs = []int32{
1063 2, // 9: ermite.AddIssueCommentResponse.comment:type_name -> ermite.IssueComment1516 2, // 9: ermite.AddIssueCommentResponse.comment:type_name -> ermite.IssueComment
1064 1, // 10: ermite.CloseIssueResponse.issue:type_name -> ermite.Issue1517 1, // 10: ermite.CloseIssueResponse.issue:type_name -> ermite.Issue
1065 1, // 11: ermite.ReopenIssueResponse.issue:type_name -> ermite.Issue1518 1, // 11: ermite.ReopenIssueResponse.issue:type_name -> ermite.Issue
1066- 3, // 12: ermite.IssueService.CreateIssue:input_type -> ermite.CreateIssueRequest1519+ 22, // 12: ermite.PendingIssue.created_at:type_name -> google.protobuf.Timestamp
1067- 5, // 13: ermite.IssueService.ListIssues:input_type -> ermite.ListIssuesRequest1520+ 15, // 13: ermite.ListPendingIssuesResponse.issues:type_name -> ermite.PendingIssue
1068- 7, // 14: ermite.IssueService.GetIssue:input_type -> ermite.GetIssueRequest1521+ 1, // 14: ermite.ApproveIssueResponse.issue:type_name -> ermite.Issue
1069- 9, // 15: ermite.IssueService.AddIssueComment:input_type -> ermite.AddIssueCommentRequest1522+ 3, // 15: ermite.IssueService.CreateIssue:input_type -> ermite.CreateIssueRequest
1070- 11, // 16: ermite.IssueService.CloseIssue:input_type -> ermite.CloseIssueRequest1523+ 5, // 16: ermite.IssueService.ListIssues:input_type -> ermite.ListIssuesRequest
1071- 13, // 17: ermite.IssueService.ReopenIssue:input_type -> ermite.ReopenIssueRequest1524+ 7, // 17: ermite.IssueService.GetIssue:input_type -> ermite.GetIssueRequest
1072- 4, // 18: ermite.IssueService.CreateIssue:output_type -> ermite.CreateIssueResponse1525+ 9, // 18: ermite.IssueService.AddIssueComment:input_type -> ermite.AddIssueCommentRequest
1073- 6, // 19: ermite.IssueService.ListIssues:output_type -> ermite.ListIssuesResponse1526+ 11, // 19: ermite.IssueService.CloseIssue:input_type -> ermite.CloseIssueRequest
1074- 8, // 20: ermite.IssueService.GetIssue:output_type -> ermite.GetIssueResponse1527+ 13, // 20: ermite.IssueService.ReopenIssue:input_type -> ermite.ReopenIssueRequest
1075- 10, // 21: ermite.IssueService.AddIssueComment:output_type -> ermite.AddIssueCommentResponse1528+ 16, // 21: ermite.IssueService.ListPendingIssues:input_type -> ermite.ListPendingIssuesRequest
1076- 12, // 22: ermite.IssueService.CloseIssue:output_type -> ermite.CloseIssueResponse1529+ 18, // 22: ermite.IssueService.ApproveIssue:input_type -> ermite.ApproveIssueRequest
1077- 14, // 23: ermite.IssueService.ReopenIssue:output_type -> ermite.ReopenIssueResponse1530+ 20, // 23: ermite.IssueService.RejectIssue:input_type -> ermite.RejectIssueRequest
1078- 18, // [18:24] is the sub-list for method output_type1531+ 4, // 24: ermite.IssueService.CreateIssue:output_type -> ermite.CreateIssueResponse
1079- 12, // [12:18] is the sub-list for method input_type1532+ 6, // 25: ermite.IssueService.ListIssues:output_type -> ermite.ListIssuesResponse
1080- 12, // [12:12] is the sub-list for extension type_name1533+ 8, // 26: ermite.IssueService.GetIssue:output_type -> ermite.GetIssueResponse
1081- 12, // [12:12] is the sub-list for extension extendee1534+ 10, // 27: ermite.IssueService.AddIssueComment:output_type -> ermite.AddIssueCommentResponse
1082- 0, // [0:12] is the sub-list for field type_name1535+ 12, // 28: ermite.IssueService.CloseIssue:output_type -> ermite.CloseIssueResponse
1536+ 14, // 29: ermite.IssueService.ReopenIssue:output_type -> ermite.ReopenIssueResponse
1537+ 17, // 30: ermite.IssueService.ListPendingIssues:output_type -> ermite.ListPendingIssuesResponse
1538+ 19, // 31: ermite.IssueService.ApproveIssue:output_type -> ermite.ApproveIssueResponse
1539+ 21, // 32: ermite.IssueService.RejectIssue:output_type -> ermite.RejectIssueResponse
1540+ 24, // [24:33] is the sub-list for method output_type
1541+ 15, // [15:24] is the sub-list for method input_type
1542+ 15, // [15:15] is the sub-list for extension type_name
1543+ 15, // [15:15] is the sub-list for extension extendee
1544+ 0, // [0:15] is the sub-list for field type_name
1083 }1545 }
1084 1546
1085 func init() { file_ermite_issue_proto_init() }1547 func init() { file_ermite_issue_proto_init() }
@@ -1093,7 +1555,7 @@ func file_ermite_issue_proto_init() {
1093 GoPackagePath: reflect.TypeOf(x{}).PkgPath(),1555 GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
1094 RawDescriptor: unsafe.Slice(unsafe.StringData(file_ermite_issue_proto_rawDesc), len(file_ermite_issue_proto_rawDesc)),1556 RawDescriptor: unsafe.Slice(unsafe.StringData(file_ermite_issue_proto_rawDesc), len(file_ermite_issue_proto_rawDesc)),
1095 NumEnums: 1,1557 NumEnums: 1,
1096- NumMessages: 14,1558+ NumMessages: 21,
1097 NumExtensions: 0,1559 NumExtensions: 0,
1098 NumServices: 1,1560 NumServices: 1,
1099 },1561 },
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