detached 2aff1f7

#2 feat(api): issue service, moderation, issues setting and issue:write scope

state
merged · 2aff1f7 · fast-forward
branch
feat/issue-form → main
opened
bnema ·
updated

Issue contract for ADR 0031 (native issues, owner Inbox, moderated public form).

What changes

  • IssueService: CreateIssue, ListIssues, GetIssue, AddIssueComment, CloseIssue and ReopenIssue, plus the owner-only moderation calls ListPendingIssues, ApproveIssue and RejectIssue (with an optional ban).
  • RepoSettings.issues / UpdateRepoRequest.issues (IssuesMode: off, owner, public). The field sits outside update_mask.
  • Scope SCOPE_ISSUE_WRITE = 11, numbered after main's SCOPE_MR_REVIEW = 10.
  • Events issue_opened|closed|reopened|commented (17–20) and issue_submitted (21). issue_submitted is delivered only on the "owner" topic and carries no content.
  • Docs: the service comment (access rules, errors, limits), the "owner" topic, the unverified author, README and docs/services.md.

Commits

Two commits, one per stage, so feat/issue-form holds both:

  • feat/issues: the issue service, the setting, the scope and the events.
  • feat/issue-form: moderation and issue_submitted.

Checks

  • buf lint passes.
  • buf breaking passes against main and against feat/issues.
  • buf generate leaves no diff.
  • go vet, go build and go test pass.

Merge order

  1. ermite MR !1 (feat/mr-notes), decided by the owner.
  2. This MR.
  3. A tag for ermite-api.
  4. The ermite MR for issues, which then drops its local replace and uses the tag.
CommitSubjectAuthorDateSignatureStatus
2aff1f7docs(api): moderation answers not found to non-owners before reading the requestbriceGPG Verified
9390908docs(api): document issue access, errors, the owner topic and the issues settingbriceGPG Verified
bfc173efeat(api): add pending issue moderation and the issue submitted eventbriceGPG Verified
3130756feat(api): add issue service, issues repo setting and issue:write scopebriceGPG Verified

Checks 4

13 files · +3034 -128
4/4
M README.md +1 -0
@@ -33,6 +33,7 @@ Status: early development.
3333 | `GitService` | refs, trees, blobs, commits and diffs |
3434 | `TokenService` | API tokens |
3535 | `MergeRequestService` | merge requests, comments, merge |
36+| `IssueService` | issues, comments, owner moderation of public submissions |
3637 | `SessionService` | web sign-in links (owner only) |
3738 | `EventService` | live event stream, resumable |
3839 | `ServerService` | version, features, signing key, OIDC key rotation |
@@ -33,6 +33,7 @@ Status: early development.
33 | `GitService` | refs, trees, blobs, commits and diffs |33 | `GitService` | refs, trees, blobs, commits and diffs |
34 | `TokenService` | API tokens |34 | `TokenService` | API tokens |
35 | `MergeRequestService` | merge requests, comments, merge |35 | `MergeRequestService` | merge requests, comments, merge |
36+| `IssueService` | issues, comments, owner moderation of public submissions |
36 | `SessionService` | web sign-in links (owner only) |37 | `SessionService` | web sign-in links (owner only) |
37 | `EventService` | live event stream, resumable |38 | `EventService` | live event stream, resumable |
38 | `ServerService` | version, features, signing key, OIDC key rotation |39 | `ServerService` | version, features, signing key, OIDC key rotation |
M docs/services.md +2 -1
@@ -6,8 +6,9 @@
66 | `GitService` | `ListRefs`, `GetTree`, `ListPaths`, `GetBlob` (streamed), `ListCommits`, `GetCommit` (structured diff), `ResolveRevision` |
77 | `TokenService` | `CreateToken` (secret shown once), `ListTokens`, `RevokeToken` |
88 | `MergeRequestService` | `CreateMergeRequest`, `ListMergeRequests`, `GetMergeRequest` (commits, diff, comments), `AddComment`, `EditComment`, `DeleteComment`, `ResolveComment`, `CloseMergeRequest`, `MergeMergeRequest`, `MarkRead` |
9+| `IssueService` | `CreateIssue`, `ListIssues`, `GetIssue` (comments), `AddIssueComment`, `CloseIssue`, `ReopenIssue`; owner only: `ListPendingIssues`, `ApproveIssue`, `RejectIssue`. Feature `issues`. Issues share numbers with merge requests |
910 | `SessionService` | `CreateLoginLink`, `SignOutEverywhere` (owner only) |
10-| `EventService` | `Watch` (streamed, resumable) |
11+| `EventService` | `Watch` (streamed, resumable). Topics `home`, `repo:<name>` and `owner` (owner rights only). Events 17–21: `issue_opened`, `issue_closed`, `issue_reopened`, `issue_commented`, and `issue_submitted` (only on `owner`, no content) |
1112 | `ServerService` | `GetVersion` (version and features), `GetSigningKey`, `RotateOIDCKey` (owner only) |
1213 | `TrustedKeyService` | `ListTrustedKeys`, `AddTrustedKey` and `RemoveTrustedKey` (owner only). `Commit.verification` carries the result of the check (feature `commit_signatures`) |
1314 | `RunService` | `ListRuns`, `GetRun`, `GetJob`, `GetJobLog` (streamed, follow), `ListAnnotations`, `GetStepSummaries`, `CancelRun`, `RerunRun`, `DispatchWorkflow`, `ListWorkflows`, `DisableWorkflow`, `EnableWorkflow`, `ListChecks`, `ListRunners`, `RemoveRunner` |
@@ -6,8 +6,9 @@
6 | `GitService` | `ListRefs`, `GetTree`, `ListPaths`, `GetBlob` (streamed), `ListCommits`, `GetCommit` (structured diff), `ResolveRevision` |6 | `GitService` | `ListRefs`, `GetTree`, `ListPaths`, `GetBlob` (streamed), `ListCommits`, `GetCommit` (structured diff), `ResolveRevision` |
7 | `TokenService` | `CreateToken` (secret shown once), `ListTokens`, `RevokeToken` |7 | `TokenService` | `CreateToken` (secret shown once), `ListTokens`, `RevokeToken` |
8 | `MergeRequestService` | `CreateMergeRequest`, `ListMergeRequests`, `GetMergeRequest` (commits, diff, comments), `AddComment`, `EditComment`, `DeleteComment`, `ResolveComment`, `CloseMergeRequest`, `MergeMergeRequest`, `MarkRead` |8 | `MergeRequestService` | `CreateMergeRequest`, `ListMergeRequests`, `GetMergeRequest` (commits, diff, comments), `AddComment`, `EditComment`, `DeleteComment`, `ResolveComment`, `CloseMergeRequest`, `MergeMergeRequest`, `MarkRead` |
9+| `IssueService` | `CreateIssue`, `ListIssues`, `GetIssue` (comments), `AddIssueComment`, `CloseIssue`, `ReopenIssue`; owner only: `ListPendingIssues`, `ApproveIssue`, `RejectIssue`. Feature `issues`. Issues share numbers with merge requests |
9 | `SessionService` | `CreateLoginLink`, `SignOutEverywhere` (owner only) |10 | `SessionService` | `CreateLoginLink`, `SignOutEverywhere` (owner only) |
10-| `EventService` | `Watch` (streamed, resumable) |11+| `EventService` | `Watch` (streamed, resumable). Topics `home`, `repo:<name>` and `owner` (owner rights only). Events 17–21: `issue_opened`, `issue_closed`, `issue_reopened`, `issue_commented`, and `issue_submitted` (only on `owner`, no content) |
11 | `ServerService` | `GetVersion` (version and features), `GetSigningKey`, `RotateOIDCKey` (owner only) |12 | `ServerService` | `GetVersion` (version and features), `GetSigningKey`, `RotateOIDCKey` (owner only) |
12 | `TrustedKeyService` | `ListTrustedKeys`, `AddTrustedKey` and `RemoveTrustedKey` (owner only). `Commit.verification` carries the result of the check (feature `commit_signatures`) |13 | `TrustedKeyService` | `ListTrustedKeys`, `AddTrustedKey` and `RemoveTrustedKey` (owner only). `Commit.verification` carries the result of the check (feature `commit_signatures`) |
13 | `RunService` | `ListRuns`, `GetRun`, `GetJob`, `GetJobLog` (streamed, follow), `ListAnnotations`, `GetStepSummaries`, `CancelRun`, `RerunRun`, `DispatchWorkflow`, `ListWorkflows`, `DisableWorkflow`, `EnableWorkflow`, `ListChecks`, `ListRunners`, `RemoveRunner` |14 | `RunService` | `ListRuns`, `GetRun`, `GetJob`, `GetJobLog` (streamed, follow), `ListAnnotations`, `GetStepSummaries`, `CancelRun`, `RerunRun`, `DispatchWorkflow`, `ListWorkflows`, `DisableWorkflow`, `EnableWorkflow`, `ListChecks`, `ListRunners`, `RemoveRunner` |
M gen/ermite/ermiteconnect/event.connect.go +12 -6
@@ -48,9 +48,12 @@ type EventServiceClient interface {
4848 // The events are repo_created, repo_updated, repo_deleted, pushed, gap and,
4949 // for merge requests, merge_request_opened, merge_request_merged,
5050 // merge_request_closed, comment_added, comment_edited, comment_deleted and
51- // comment_resolved (a thread resolved or reopened). Each one is delivered on the
52- // "repo:<name>" topic of its repository and on "home", subject to the
53- // visibility of the repository.
51+ // comment_resolved (a thread resolved or reopened); for issues, issue_opened,
52+ // issue_closed, issue_reopened and issue_commented, and issue_submitted. Each
53+ // one is delivered on the "repo:<name>" topic of its repository and on
54+ // "home", subject to the visibility of the repository, except
55+ // issue_submitted: it is delivered only on the "owner" topic, never on
56+ // "home" or "repo:<name>", and carries no content (see IssueSubmitted).
5457 //
5558 // Servers with the feature "ci.runs" also publish run_updated and
5659 // job_updated, only on the "repo:<name>" topic of their repository (never on
@@ -121,9 +124,12 @@ type EventServiceHandler interface {
121124 // The events are repo_created, repo_updated, repo_deleted, pushed, gap and,
122125 // for merge requests, merge_request_opened, merge_request_merged,
123126 // merge_request_closed, comment_added, comment_edited, comment_deleted and
124- // comment_resolved (a thread resolved or reopened). Each one is delivered on the
125- // "repo:<name>" topic of its repository and on "home", subject to the
126- // visibility of the repository.
127+ // comment_resolved (a thread resolved or reopened); for issues, issue_opened,
128+ // issue_closed, issue_reopened and issue_commented, and issue_submitted. Each
129+ // one is delivered on the "repo:<name>" topic of its repository and on
130+ // "home", subject to the visibility of the repository, except
131+ // issue_submitted: it is delivered only on the "owner" topic, never on
132+ // "home" or "repo:<name>", and carries no content (see IssueSubmitted).
127133 //
128134 // Servers with the feature "ci.runs" also publish run_updated and
129135 // job_updated, only on the "repo:<name>" topic of their repository (never on
@@ -48,9 +48,12 @@ type EventServiceClient interface {
48 // The events are repo_created, repo_updated, repo_deleted, pushed, gap and,48 // The events are repo_created, repo_updated, repo_deleted, pushed, gap and,
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). Each one is delivered on the51+ // comment_resolved (a thread resolved or reopened); for issues, issue_opened,
52- // "repo:<name>" topic of its repository and on "home", subject to the52+ // issue_closed, issue_reopened and issue_commented, and issue_submitted. Each
53- // visibility of the repository.53+ // one is delivered on the "repo:<name>" topic of its repository and on
54+ // "home", subject to the visibility of the repository, except
55+ // issue_submitted: it is delivered only on the "owner" topic, never on
56+ // "home" or "repo:<name>", and carries no content (see IssueSubmitted).
54 //57 //
55 // Servers with the feature "ci.runs" also publish run_updated and58 // Servers with the feature "ci.runs" also publish run_updated and
56 // job_updated, only on the "repo:<name>" topic of their repository (never on59 // job_updated, only on the "repo:<name>" topic of their repository (never on
@@ -121,9 +124,12 @@ type EventServiceHandler interface {
121 // The events are repo_created, repo_updated, repo_deleted, pushed, gap and,124 // The events are repo_created, repo_updated, repo_deleted, pushed, gap and,
122 // for merge requests, merge_request_opened, merge_request_merged,125 // for merge requests, merge_request_opened, merge_request_merged,
123 // merge_request_closed, comment_added, comment_edited, comment_deleted and126 // merge_request_closed, comment_added, comment_edited, comment_deleted and
124- // comment_resolved (a thread resolved or reopened). Each one is delivered on the127+ // comment_resolved (a thread resolved or reopened); for issues, issue_opened,
125- // "repo:<name>" topic of its repository and on "home", subject to the128+ // issue_closed, issue_reopened and issue_commented, and issue_submitted. Each
126- // visibility of the repository.129+ // one is delivered on the "repo:<name>" topic of its repository and on
130+ // "home", subject to the visibility of the repository, except
131+ // issue_submitted: it is delivered only on the "owner" topic, never on
132+ // "home" or "repo:<name>", and carries no content (see IssueSubmitted).
127 //133 //
128 // Servers with the feature "ci.runs" also publish run_updated and134 // Servers with the feature "ci.runs" also publish run_updated and
129 // job_updated, only on the "repo:<name>" topic of their repository (never on135 // job_updated, only on the "repo:<name>" topic of their repository (never on
A gen/ermite/ermiteconnect/issue.connect.go +364 -0
@@ -0,0 +1,364 @@
1+// SPDX-License-Identifier: MIT
2+
3+// Code generated by protoc-gen-connect-go. DO NOT EDIT.
4+//
5+// Source: ermite/issue.proto
6+
7+package ermiteconnect
8+
9+import (
10+ ermite "bnema.dev/ermite-api/gen/ermite"
11+ connect "connectrpc.com/connect"
12+ context "context"
13+ errors "errors"
14+ http "net/http"
15+ strings "strings"
16+)
17+
18+// This is a compile-time assertion to ensure that this generated file and the connect package are
19+// compatible. If you get a compiler error that this constant is not defined, this code was
20+// generated with a version of connect newer than the one compiled into your binary. You can fix the
21+// problem by either regenerating this code with an older version of connect or updating the connect
22+// version compiled into your binary.
23+const _ = connect.IsAtLeastVersion1_13_0
24+
25+const (
26+ // IssueServiceName is the fully-qualified name of the IssueService service.
27+ IssueServiceName = "ermite.IssueService"
28+)
29+
30+// These constants are the fully-qualified names of the RPCs defined in this package. They're
31+// exposed at runtime as Spec.Procedure and as the final two segments of the HTTP route.
32+//
33+// Note that these are different from the fully-qualified method names used by
34+// google.golang.org/protobuf/reflect/protoreflect. To convert from these constants to
35+// reflection-formatted method names, remove the leading slash and convert the remaining slash to a
36+// period.
37+const (
38+ // IssueServiceCreateIssueProcedure is the fully-qualified name of the IssueService's CreateIssue
39+ // RPC.
40+ IssueServiceCreateIssueProcedure = "/ermite.IssueService/CreateIssue"
41+ // IssueServiceListIssuesProcedure is the fully-qualified name of the IssueService's ListIssues RPC.
42+ IssueServiceListIssuesProcedure = "/ermite.IssueService/ListIssues"
43+ // IssueServiceGetIssueProcedure is the fully-qualified name of the IssueService's GetIssue RPC.
44+ IssueServiceGetIssueProcedure = "/ermite.IssueService/GetIssue"
45+ // IssueServiceAddIssueCommentProcedure is the fully-qualified name of the IssueService's
46+ // AddIssueComment RPC.
47+ IssueServiceAddIssueCommentProcedure = "/ermite.IssueService/AddIssueComment"
48+ // IssueServiceCloseIssueProcedure is the fully-qualified name of the IssueService's CloseIssue RPC.
49+ IssueServiceCloseIssueProcedure = "/ermite.IssueService/CloseIssue"
50+ // IssueServiceReopenIssueProcedure is the fully-qualified name of the IssueService's ReopenIssue
51+ // RPC.
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"
62+)
63+
64+// IssueServiceClient is a client for the ermite.IssueService service.
65+type IssueServiceClient interface {
66+ // CreateIssue opens an issue. Needs the issue:write scope.
67+ CreateIssue(context.Context, *connect.Request[ermite.CreateIssueRequest]) (*connect.Response[ermite.CreateIssueResponse], error)
68+ // ListIssues lists issues, highest number first, optionally in one state.
69+ ListIssues(context.Context, *connect.Request[ermite.ListIssuesRequest]) (*connect.Response[ermite.ListIssuesResponse], error)
70+ // GetIssue returns an issue with its comments.
71+ GetIssue(context.Context, *connect.Request[ermite.GetIssueRequest]) (*connect.Response[ermite.GetIssueResponse], error)
72+ // AddIssueComment adds a comment. Needs the issue:write scope.
73+ AddIssueComment(context.Context, *connect.Request[ermite.AddIssueCommentRequest]) (*connect.Response[ermite.AddIssueCommentResponse], error)
74+ // CloseIssue closes an issue. Needs the issue:write scope.
75+ CloseIssue(context.Context, *connect.Request[ermite.CloseIssueRequest]) (*connect.Response[ermite.CloseIssueResponse], error)
76+ // ReopenIssue reopens an issue. Needs the issue:write scope.
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)
87+}
88+
89+// NewIssueServiceClient constructs a client for the ermite.IssueService service. By default, it
90+// uses the Connect protocol with the binary Protobuf Codec, asks for gzipped responses, and sends
91+// uncompressed requests. To use the gRPC or gRPC-Web protocols, supply the connect.WithGRPC() or
92+// connect.WithGRPCWeb() options.
93+//
94+// The URL supplied here should be the base URL for the Connect or gRPC server (for example,
95+// http://api.acme.com or https://acme.com/grpc).
96+func NewIssueServiceClient(httpClient connect.HTTPClient, baseURL string, opts ...connect.ClientOption) IssueServiceClient {
97+ baseURL = strings.TrimRight(baseURL, "/")
98+ issueServiceMethods := ermite.File_ermite_issue_proto.Services().ByName("IssueService").Methods()
99+ return &issueServiceClient{
100+ createIssue: connect.NewClient[ermite.CreateIssueRequest, ermite.CreateIssueResponse](
101+ httpClient,
102+ baseURL+IssueServiceCreateIssueProcedure,
103+ connect.WithSchema(issueServiceMethods.ByName("CreateIssue")),
104+ connect.WithClientOptions(opts...),
105+ ),
106+ listIssues: connect.NewClient[ermite.ListIssuesRequest, ermite.ListIssuesResponse](
107+ httpClient,
108+ baseURL+IssueServiceListIssuesProcedure,
109+ connect.WithSchema(issueServiceMethods.ByName("ListIssues")),
110+ connect.WithClientOptions(opts...),
111+ ),
112+ getIssue: connect.NewClient[ermite.GetIssueRequest, ermite.GetIssueResponse](
113+ httpClient,
114+ baseURL+IssueServiceGetIssueProcedure,
115+ connect.WithSchema(issueServiceMethods.ByName("GetIssue")),
116+ connect.WithClientOptions(opts...),
117+ ),
118+ addIssueComment: connect.NewClient[ermite.AddIssueCommentRequest, ermite.AddIssueCommentResponse](
119+ httpClient,
120+ baseURL+IssueServiceAddIssueCommentProcedure,
121+ connect.WithSchema(issueServiceMethods.ByName("AddIssueComment")),
122+ connect.WithClientOptions(opts...),
123+ ),
124+ closeIssue: connect.NewClient[ermite.CloseIssueRequest, ermite.CloseIssueResponse](
125+ httpClient,
126+ baseURL+IssueServiceCloseIssueProcedure,
127+ connect.WithSchema(issueServiceMethods.ByName("CloseIssue")),
128+ connect.WithClientOptions(opts...),
129+ ),
130+ reopenIssue: connect.NewClient[ermite.ReopenIssueRequest, ermite.ReopenIssueResponse](
131+ httpClient,
132+ baseURL+IssueServiceReopenIssueProcedure,
133+ connect.WithSchema(issueServiceMethods.ByName("ReopenIssue")),
134+ connect.WithClientOptions(opts...),
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+ ),
154+ }
155+}
156+
157+// issueServiceClient implements IssueServiceClient.
158+type issueServiceClient struct {
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]
168+}
169+
170+// CreateIssue calls ermite.IssueService.CreateIssue.
171+func (c *issueServiceClient) CreateIssue(ctx context.Context, req *connect.Request[ermite.CreateIssueRequest]) (*connect.Response[ermite.CreateIssueResponse], error) {
172+ return c.createIssue.CallUnary(ctx, req)
173+}
174+
175+// ListIssues calls ermite.IssueService.ListIssues.
176+func (c *issueServiceClient) ListIssues(ctx context.Context, req *connect.Request[ermite.ListIssuesRequest]) (*connect.Response[ermite.ListIssuesResponse], error) {
177+ return c.listIssues.CallUnary(ctx, req)
178+}
179+
180+// GetIssue calls ermite.IssueService.GetIssue.
181+func (c *issueServiceClient) GetIssue(ctx context.Context, req *connect.Request[ermite.GetIssueRequest]) (*connect.Response[ermite.GetIssueResponse], error) {
182+ return c.getIssue.CallUnary(ctx, req)
183+}
184+
185+// AddIssueComment calls ermite.IssueService.AddIssueComment.
186+func (c *issueServiceClient) AddIssueComment(ctx context.Context, req *connect.Request[ermite.AddIssueCommentRequest]) (*connect.Response[ermite.AddIssueCommentResponse], error) {
187+ return c.addIssueComment.CallUnary(ctx, req)
188+}
189+
190+// CloseIssue calls ermite.IssueService.CloseIssue.
191+func (c *issueServiceClient) CloseIssue(ctx context.Context, req *connect.Request[ermite.CloseIssueRequest]) (*connect.Response[ermite.CloseIssueResponse], error) {
192+ return c.closeIssue.CallUnary(ctx, req)
193+}
194+
195+// ReopenIssue calls ermite.IssueService.ReopenIssue.
196+func (c *issueServiceClient) ReopenIssue(ctx context.Context, req *connect.Request[ermite.ReopenIssueRequest]) (*connect.Response[ermite.ReopenIssueResponse], error) {
197+ return c.reopenIssue.CallUnary(ctx, req)
198+}
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+
215+// IssueServiceHandler is an implementation of the ermite.IssueService service.
216+type IssueServiceHandler interface {
217+ // CreateIssue opens an issue. Needs the issue:write scope.
218+ CreateIssue(context.Context, *connect.Request[ermite.CreateIssueRequest]) (*connect.Response[ermite.CreateIssueResponse], error)
219+ // ListIssues lists issues, highest number first, optionally in one state.
220+ ListIssues(context.Context, *connect.Request[ermite.ListIssuesRequest]) (*connect.Response[ermite.ListIssuesResponse], error)
221+ // GetIssue returns an issue with its comments.
222+ GetIssue(context.Context, *connect.Request[ermite.GetIssueRequest]) (*connect.Response[ermite.GetIssueResponse], error)
223+ // AddIssueComment adds a comment. Needs the issue:write scope.
224+ AddIssueComment(context.Context, *connect.Request[ermite.AddIssueCommentRequest]) (*connect.Response[ermite.AddIssueCommentResponse], error)
225+ // CloseIssue closes an issue. Needs the issue:write scope.
226+ CloseIssue(context.Context, *connect.Request[ermite.CloseIssueRequest]) (*connect.Response[ermite.CloseIssueResponse], error)
227+ // ReopenIssue reopens an issue. Needs the issue:write scope.
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)
238+}
239+
240+// NewIssueServiceHandler builds an HTTP handler from the service implementation. It returns the
241+// path on which to mount the handler and the handler itself.
242+//
243+// By default, handlers support the Connect, gRPC, and gRPC-Web protocols with the binary Protobuf
244+// and JSON codecs. They also support gzip compression.
245+func NewIssueServiceHandler(svc IssueServiceHandler, opts ...connect.HandlerOption) (string, http.Handler) {
246+ issueServiceMethods := ermite.File_ermite_issue_proto.Services().ByName("IssueService").Methods()
247+ issueServiceCreateIssueHandler := connect.NewUnaryHandler(
248+ IssueServiceCreateIssueProcedure,
249+ svc.CreateIssue,
250+ connect.WithSchema(issueServiceMethods.ByName("CreateIssue")),
251+ connect.WithHandlerOptions(opts...),
252+ )
253+ issueServiceListIssuesHandler := connect.NewUnaryHandler(
254+ IssueServiceListIssuesProcedure,
255+ svc.ListIssues,
256+ connect.WithSchema(issueServiceMethods.ByName("ListIssues")),
257+ connect.WithHandlerOptions(opts...),
258+ )
259+ issueServiceGetIssueHandler := connect.NewUnaryHandler(
260+ IssueServiceGetIssueProcedure,
261+ svc.GetIssue,
262+ connect.WithSchema(issueServiceMethods.ByName("GetIssue")),
263+ connect.WithHandlerOptions(opts...),
264+ )
265+ issueServiceAddIssueCommentHandler := connect.NewUnaryHandler(
266+ IssueServiceAddIssueCommentProcedure,
267+ svc.AddIssueComment,
268+ connect.WithSchema(issueServiceMethods.ByName("AddIssueComment")),
269+ connect.WithHandlerOptions(opts...),
270+ )
271+ issueServiceCloseIssueHandler := connect.NewUnaryHandler(
272+ IssueServiceCloseIssueProcedure,
273+ svc.CloseIssue,
274+ connect.WithSchema(issueServiceMethods.ByName("CloseIssue")),
275+ connect.WithHandlerOptions(opts...),
276+ )
277+ issueServiceReopenIssueHandler := connect.NewUnaryHandler(
278+ IssueServiceReopenIssueProcedure,
279+ svc.ReopenIssue,
280+ connect.WithSchema(issueServiceMethods.ByName("ReopenIssue")),
281+ connect.WithHandlerOptions(opts...),
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+ )
301+ return "/ermite.IssueService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
302+ switch r.URL.Path {
303+ case IssueServiceCreateIssueProcedure:
304+ issueServiceCreateIssueHandler.ServeHTTP(w, r)
305+ case IssueServiceListIssuesProcedure:
306+ issueServiceListIssuesHandler.ServeHTTP(w, r)
307+ case IssueServiceGetIssueProcedure:
308+ issueServiceGetIssueHandler.ServeHTTP(w, r)
309+ case IssueServiceAddIssueCommentProcedure:
310+ issueServiceAddIssueCommentHandler.ServeHTTP(w, r)
311+ case IssueServiceCloseIssueProcedure:
312+ issueServiceCloseIssueHandler.ServeHTTP(w, r)
313+ case IssueServiceReopenIssueProcedure:
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)
321+ default:
322+ http.NotFound(w, r)
323+ }
324+ })
325+}
326+
327+// UnimplementedIssueServiceHandler returns CodeUnimplemented from all methods.
328+type UnimplementedIssueServiceHandler struct{}
329+
330+func (UnimplementedIssueServiceHandler) CreateIssue(context.Context, *connect.Request[ermite.CreateIssueRequest]) (*connect.Response[ermite.CreateIssueResponse], error) {
331+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.IssueService.CreateIssue is not implemented"))
332+}
333+
334+func (UnimplementedIssueServiceHandler) ListIssues(context.Context, *connect.Request[ermite.ListIssuesRequest]) (*connect.Response[ermite.ListIssuesResponse], error) {
335+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.IssueService.ListIssues is not implemented"))
336+}
337+
338+func (UnimplementedIssueServiceHandler) GetIssue(context.Context, *connect.Request[ermite.GetIssueRequest]) (*connect.Response[ermite.GetIssueResponse], error) {
339+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.IssueService.GetIssue is not implemented"))
340+}
341+
342+func (UnimplementedIssueServiceHandler) AddIssueComment(context.Context, *connect.Request[ermite.AddIssueCommentRequest]) (*connect.Response[ermite.AddIssueCommentResponse], error) {
343+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.IssueService.AddIssueComment is not implemented"))
344+}
345+
346+func (UnimplementedIssueServiceHandler) CloseIssue(context.Context, *connect.Request[ermite.CloseIssueRequest]) (*connect.Response[ermite.CloseIssueResponse], error) {
347+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.IssueService.CloseIssue is not implemented"))
348+}
349+
350+func (UnimplementedIssueServiceHandler) ReopenIssue(context.Context, *connect.Request[ermite.ReopenIssueRequest]) (*connect.Response[ermite.ReopenIssueResponse], error) {
351+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.IssueService.ReopenIssue is not implemented"))
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+}
@@ -0,0 +1,364 @@
1+// SPDX-License-Identifier: MIT
2+
3+// Code generated by protoc-gen-connect-go. DO NOT EDIT.
4+//
5+// Source: ermite/issue.proto
6+
7+package ermiteconnect
8+
9+import (
10+ ermite "bnema.dev/ermite-api/gen/ermite"
11+ connect "connectrpc.com/connect"
12+ context "context"
13+ errors "errors"
14+ http "net/http"
15+ strings "strings"
16+)
17+
18+// This is a compile-time assertion to ensure that this generated file and the connect package are
19+// compatible. If you get a compiler error that this constant is not defined, this code was
20+// generated with a version of connect newer than the one compiled into your binary. You can fix the
21+// problem by either regenerating this code with an older version of connect or updating the connect
22+// version compiled into your binary.
23+const _ = connect.IsAtLeastVersion1_13_0
24+
25+const (
26+ // IssueServiceName is the fully-qualified name of the IssueService service.
27+ IssueServiceName = "ermite.IssueService"
28+)
29+
30+// These constants are the fully-qualified names of the RPCs defined in this package. They're
31+// exposed at runtime as Spec.Procedure and as the final two segments of the HTTP route.
32+//
33+// Note that these are different from the fully-qualified method names used by
34+// google.golang.org/protobuf/reflect/protoreflect. To convert from these constants to
35+// reflection-formatted method names, remove the leading slash and convert the remaining slash to a
36+// period.
37+const (
38+ // IssueServiceCreateIssueProcedure is the fully-qualified name of the IssueService's CreateIssue
39+ // RPC.
40+ IssueServiceCreateIssueProcedure = "/ermite.IssueService/CreateIssue"
41+ // IssueServiceListIssuesProcedure is the fully-qualified name of the IssueService's ListIssues RPC.
42+ IssueServiceListIssuesProcedure = "/ermite.IssueService/ListIssues"
43+ // IssueServiceGetIssueProcedure is the fully-qualified name of the IssueService's GetIssue RPC.
44+ IssueServiceGetIssueProcedure = "/ermite.IssueService/GetIssue"
45+ // IssueServiceAddIssueCommentProcedure is the fully-qualified name of the IssueService's
46+ // AddIssueComment RPC.
47+ IssueServiceAddIssueCommentProcedure = "/ermite.IssueService/AddIssueComment"
48+ // IssueServiceCloseIssueProcedure is the fully-qualified name of the IssueService's CloseIssue RPC.
49+ IssueServiceCloseIssueProcedure = "/ermite.IssueService/CloseIssue"
50+ // IssueServiceReopenIssueProcedure is the fully-qualified name of the IssueService's ReopenIssue
51+ // RPC.
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"
62+)
63+
64+// IssueServiceClient is a client for the ermite.IssueService service.
65+type IssueServiceClient interface {
66+ // CreateIssue opens an issue. Needs the issue:write scope.
67+ CreateIssue(context.Context, *connect.Request[ermite.CreateIssueRequest]) (*connect.Response[ermite.CreateIssueResponse], error)
68+ // ListIssues lists issues, highest number first, optionally in one state.
69+ ListIssues(context.Context, *connect.Request[ermite.ListIssuesRequest]) (*connect.Response[ermite.ListIssuesResponse], error)
70+ // GetIssue returns an issue with its comments.
71+ GetIssue(context.Context, *connect.Request[ermite.GetIssueRequest]) (*connect.Response[ermite.GetIssueResponse], error)
72+ // AddIssueComment adds a comment. Needs the issue:write scope.
73+ AddIssueComment(context.Context, *connect.Request[ermite.AddIssueCommentRequest]) (*connect.Response[ermite.AddIssueCommentResponse], error)
74+ // CloseIssue closes an issue. Needs the issue:write scope.
75+ CloseIssue(context.Context, *connect.Request[ermite.CloseIssueRequest]) (*connect.Response[ermite.CloseIssueResponse], error)
76+ // ReopenIssue reopens an issue. Needs the issue:write scope.
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)
87+}
88+
89+// NewIssueServiceClient constructs a client for the ermite.IssueService service. By default, it
90+// uses the Connect protocol with the binary Protobuf Codec, asks for gzipped responses, and sends
91+// uncompressed requests. To use the gRPC or gRPC-Web protocols, supply the connect.WithGRPC() or
92+// connect.WithGRPCWeb() options.
93+//
94+// The URL supplied here should be the base URL for the Connect or gRPC server (for example,
95+// http://api.acme.com or https://acme.com/grpc).
96+func NewIssueServiceClient(httpClient connect.HTTPClient, baseURL string, opts ...connect.ClientOption) IssueServiceClient {
97+ baseURL = strings.TrimRight(baseURL, "/")
98+ issueServiceMethods := ermite.File_ermite_issue_proto.Services().ByName("IssueService").Methods()
99+ return &issueServiceClient{
100+ createIssue: connect.NewClient[ermite.CreateIssueRequest, ermite.CreateIssueResponse](
101+ httpClient,
102+ baseURL+IssueServiceCreateIssueProcedure,
103+ connect.WithSchema(issueServiceMethods.ByName("CreateIssue")),
104+ connect.WithClientOptions(opts...),
105+ ),
106+ listIssues: connect.NewClient[ermite.ListIssuesRequest, ermite.ListIssuesResponse](
107+ httpClient,
108+ baseURL+IssueServiceListIssuesProcedure,
109+ connect.WithSchema(issueServiceMethods.ByName("ListIssues")),
110+ connect.WithClientOptions(opts...),
111+ ),
112+ getIssue: connect.NewClient[ermite.GetIssueRequest, ermite.GetIssueResponse](
113+ httpClient,
114+ baseURL+IssueServiceGetIssueProcedure,
115+ connect.WithSchema(issueServiceMethods.ByName("GetIssue")),
116+ connect.WithClientOptions(opts...),
117+ ),
118+ addIssueComment: connect.NewClient[ermite.AddIssueCommentRequest, ermite.AddIssueCommentResponse](
119+ httpClient,
120+ baseURL+IssueServiceAddIssueCommentProcedure,
121+ connect.WithSchema(issueServiceMethods.ByName("AddIssueComment")),
122+ connect.WithClientOptions(opts...),
123+ ),
124+ closeIssue: connect.NewClient[ermite.CloseIssueRequest, ermite.CloseIssueResponse](
125+ httpClient,
126+ baseURL+IssueServiceCloseIssueProcedure,
127+ connect.WithSchema(issueServiceMethods.ByName("CloseIssue")),
128+ connect.WithClientOptions(opts...),
129+ ),
130+ reopenIssue: connect.NewClient[ermite.ReopenIssueRequest, ermite.ReopenIssueResponse](
131+ httpClient,
132+ baseURL+IssueServiceReopenIssueProcedure,
133+ connect.WithSchema(issueServiceMethods.ByName("ReopenIssue")),
134+ connect.WithClientOptions(opts...),
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+ ),
154+ }
155+}
156+
157+// issueServiceClient implements IssueServiceClient.
158+type issueServiceClient struct {
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]
168+}
169+
170+// CreateIssue calls ermite.IssueService.CreateIssue.
171+func (c *issueServiceClient) CreateIssue(ctx context.Context, req *connect.Request[ermite.CreateIssueRequest]) (*connect.Response[ermite.CreateIssueResponse], error) {
172+ return c.createIssue.CallUnary(ctx, req)
173+}
174+
175+// ListIssues calls ermite.IssueService.ListIssues.
176+func (c *issueServiceClient) ListIssues(ctx context.Context, req *connect.Request[ermite.ListIssuesRequest]) (*connect.Response[ermite.ListIssuesResponse], error) {
177+ return c.listIssues.CallUnary(ctx, req)
178+}
179+
180+// GetIssue calls ermite.IssueService.GetIssue.
181+func (c *issueServiceClient) GetIssue(ctx context.Context, req *connect.Request[ermite.GetIssueRequest]) (*connect.Response[ermite.GetIssueResponse], error) {
182+ return c.getIssue.CallUnary(ctx, req)
183+}
184+
185+// AddIssueComment calls ermite.IssueService.AddIssueComment.
186+func (c *issueServiceClient) AddIssueComment(ctx context.Context, req *connect.Request[ermite.AddIssueCommentRequest]) (*connect.Response[ermite.AddIssueCommentResponse], error) {
187+ return c.addIssueComment.CallUnary(ctx, req)
188+}
189+
190+// CloseIssue calls ermite.IssueService.CloseIssue.
191+func (c *issueServiceClient) CloseIssue(ctx context.Context, req *connect.Request[ermite.CloseIssueRequest]) (*connect.Response[ermite.CloseIssueResponse], error) {
192+ return c.closeIssue.CallUnary(ctx, req)
193+}
194+
195+// ReopenIssue calls ermite.IssueService.ReopenIssue.
196+func (c *issueServiceClient) ReopenIssue(ctx context.Context, req *connect.Request[ermite.ReopenIssueRequest]) (*connect.Response[ermite.ReopenIssueResponse], error) {
197+ return c.reopenIssue.CallUnary(ctx, req)
198+}
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+
215+// IssueServiceHandler is an implementation of the ermite.IssueService service.
216+type IssueServiceHandler interface {
217+ // CreateIssue opens an issue. Needs the issue:write scope.
218+ CreateIssue(context.Context, *connect.Request[ermite.CreateIssueRequest]) (*connect.Response[ermite.CreateIssueResponse], error)
219+ // ListIssues lists issues, highest number first, optionally in one state.
220+ ListIssues(context.Context, *connect.Request[ermite.ListIssuesRequest]) (*connect.Response[ermite.ListIssuesResponse], error)
221+ // GetIssue returns an issue with its comments.
222+ GetIssue(context.Context, *connect.Request[ermite.GetIssueRequest]) (*connect.Response[ermite.GetIssueResponse], error)
223+ // AddIssueComment adds a comment. Needs the issue:write scope.
224+ AddIssueComment(context.Context, *connect.Request[ermite.AddIssueCommentRequest]) (*connect.Response[ermite.AddIssueCommentResponse], error)
225+ // CloseIssue closes an issue. Needs the issue:write scope.
226+ CloseIssue(context.Context, *connect.Request[ermite.CloseIssueRequest]) (*connect.Response[ermite.CloseIssueResponse], error)
227+ // ReopenIssue reopens an issue. Needs the issue:write scope.
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)
238+}
239+
240+// NewIssueServiceHandler builds an HTTP handler from the service implementation. It returns the
241+// path on which to mount the handler and the handler itself.
242+//
243+// By default, handlers support the Connect, gRPC, and gRPC-Web protocols with the binary Protobuf
244+// and JSON codecs. They also support gzip compression.
245+func NewIssueServiceHandler(svc IssueServiceHandler, opts ...connect.HandlerOption) (string, http.Handler) {
246+ issueServiceMethods := ermite.File_ermite_issue_proto.Services().ByName("IssueService").Methods()
247+ issueServiceCreateIssueHandler := connect.NewUnaryHandler(
248+ IssueServiceCreateIssueProcedure,
249+ svc.CreateIssue,
250+ connect.WithSchema(issueServiceMethods.ByName("CreateIssue")),
251+ connect.WithHandlerOptions(opts...),
252+ )
253+ issueServiceListIssuesHandler := connect.NewUnaryHandler(
254+ IssueServiceListIssuesProcedure,
255+ svc.ListIssues,
256+ connect.WithSchema(issueServiceMethods.ByName("ListIssues")),
257+ connect.WithHandlerOptions(opts...),
258+ )
259+ issueServiceGetIssueHandler := connect.NewUnaryHandler(
260+ IssueServiceGetIssueProcedure,
261+ svc.GetIssue,
262+ connect.WithSchema(issueServiceMethods.ByName("GetIssue")),
263+ connect.WithHandlerOptions(opts...),
264+ )
265+ issueServiceAddIssueCommentHandler := connect.NewUnaryHandler(
266+ IssueServiceAddIssueCommentProcedure,
267+ svc.AddIssueComment,
268+ connect.WithSchema(issueServiceMethods.ByName("AddIssueComment")),
269+ connect.WithHandlerOptions(opts...),
270+ )
271+ issueServiceCloseIssueHandler := connect.NewUnaryHandler(
272+ IssueServiceCloseIssueProcedure,
273+ svc.CloseIssue,
274+ connect.WithSchema(issueServiceMethods.ByName("CloseIssue")),
275+ connect.WithHandlerOptions(opts...),
276+ )
277+ issueServiceReopenIssueHandler := connect.NewUnaryHandler(
278+ IssueServiceReopenIssueProcedure,
279+ svc.ReopenIssue,
280+ connect.WithSchema(issueServiceMethods.ByName("ReopenIssue")),
281+ connect.WithHandlerOptions(opts...),
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+ )
301+ return "/ermite.IssueService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
302+ switch r.URL.Path {
303+ case IssueServiceCreateIssueProcedure:
304+ issueServiceCreateIssueHandler.ServeHTTP(w, r)
305+ case IssueServiceListIssuesProcedure:
306+ issueServiceListIssuesHandler.ServeHTTP(w, r)
307+ case IssueServiceGetIssueProcedure:
308+ issueServiceGetIssueHandler.ServeHTTP(w, r)
309+ case IssueServiceAddIssueCommentProcedure:
310+ issueServiceAddIssueCommentHandler.ServeHTTP(w, r)
311+ case IssueServiceCloseIssueProcedure:
312+ issueServiceCloseIssueHandler.ServeHTTP(w, r)
313+ case IssueServiceReopenIssueProcedure:
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)
321+ default:
322+ http.NotFound(w, r)
323+ }
324+ })
325+}
326+
327+// UnimplementedIssueServiceHandler returns CodeUnimplemented from all methods.
328+type UnimplementedIssueServiceHandler struct{}
329+
330+func (UnimplementedIssueServiceHandler) CreateIssue(context.Context, *connect.Request[ermite.CreateIssueRequest]) (*connect.Response[ermite.CreateIssueResponse], error) {
331+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.IssueService.CreateIssue is not implemented"))
332+}
333+
334+func (UnimplementedIssueServiceHandler) ListIssues(context.Context, *connect.Request[ermite.ListIssuesRequest]) (*connect.Response[ermite.ListIssuesResponse], error) {
335+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.IssueService.ListIssues is not implemented"))
336+}
337+
338+func (UnimplementedIssueServiceHandler) GetIssue(context.Context, *connect.Request[ermite.GetIssueRequest]) (*connect.Response[ermite.GetIssueResponse], error) {
339+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.IssueService.GetIssue is not implemented"))
340+}
341+
342+func (UnimplementedIssueServiceHandler) AddIssueComment(context.Context, *connect.Request[ermite.AddIssueCommentRequest]) (*connect.Response[ermite.AddIssueCommentResponse], error) {
343+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.IssueService.AddIssueComment is not implemented"))
344+}
345+
346+func (UnimplementedIssueServiceHandler) CloseIssue(context.Context, *connect.Request[ermite.CloseIssueRequest]) (*connect.Response[ermite.CloseIssueResponse], error) {
347+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.IssueService.CloseIssue is not implemented"))
348+}
349+
350+func (UnimplementedIssueServiceHandler) ReopenIssue(context.Context, *connect.Request[ermite.ReopenIssueRequest]) (*connect.Response[ermite.ReopenIssueResponse], error) {
351+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.IssueService.ReopenIssue is not implemented"))
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 +445 -51 generated file: collapsed
A gen/ermite/issue.pb.go +1574 -0
@@ -0,0 +1,1574 @@
1+// SPDX-License-Identifier: MIT
2+
3+// Code generated by protoc-gen-go. DO NOT EDIT.
4+// versions:
5+// protoc-gen-go v1.36.12
6+// protoc (unknown)
7+// source: ermite/issue.proto
8+
9+package ermite
10+
11+import (
12+ protoreflect "google.golang.org/protobuf/reflect/protoreflect"
13+ protoimpl "google.golang.org/protobuf/runtime/protoimpl"
14+ timestamppb "google.golang.org/protobuf/types/known/timestamppb"
15+ reflect "reflect"
16+ sync "sync"
17+ unsafe "unsafe"
18+)
19+
20+const (
21+ // Verify that this generated code is sufficiently up-to-date.
22+ _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
23+ // Verify that runtime/protoimpl is sufficiently up-to-date.
24+ _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
25+)
26+
27+// IssueState is where an issue is in its life.
28+type IssueState int32
29+
30+const (
31+ // Not set. In ListIssuesRequest.state it means every state. Never returned
32+ // by the server.
33+ IssueState_ISSUE_STATE_UNSPECIFIED IssueState = 0
34+ // The issue is open.
35+ IssueState_ISSUE_STATE_OPEN IssueState = 1
36+ // The issue is closed.
37+ IssueState_ISSUE_STATE_CLOSED IssueState = 2
38+ // Reserved for later use: awaiting moderation, owner-only.
39+ IssueState_ISSUE_STATE_PENDING IssueState = 3
40+)
41+
42+// Enum value maps for IssueState.
43+var (
44+ IssueState_name = map[int32]string{
45+ 0: "ISSUE_STATE_UNSPECIFIED",
46+ 1: "ISSUE_STATE_OPEN",
47+ 2: "ISSUE_STATE_CLOSED",
48+ 3: "ISSUE_STATE_PENDING",
49+ }
50+ IssueState_value = map[string]int32{
51+ "ISSUE_STATE_UNSPECIFIED": 0,
52+ "ISSUE_STATE_OPEN": 1,
53+ "ISSUE_STATE_CLOSED": 2,
54+ "ISSUE_STATE_PENDING": 3,
55+ }
56+)
57+
58+func (x IssueState) Enum() *IssueState {
59+ p := new(IssueState)
60+ *p = x
61+ return p
62+}
63+
64+func (x IssueState) String() string {
65+ return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
66+}
67+
68+func (IssueState) Descriptor() protoreflect.EnumDescriptor {
69+ return file_ermite_issue_proto_enumTypes[0].Descriptor()
70+}
71+
72+func (IssueState) Type() protoreflect.EnumType {
73+ return &file_ermite_issue_proto_enumTypes[0]
74+}
75+
76+func (x IssueState) Number() protoreflect.EnumNumber {
77+ return protoreflect.EnumNumber(x)
78+}
79+
80+// Deprecated: Use IssueState.Descriptor instead.
81+func (IssueState) EnumDescriptor() ([]byte, []int) {
82+ return file_ermite_issue_proto_rawDescGZIP(), []int{0}
83+}
84+
85+// Issue is a discussion in a repository.
86+type Issue struct {
87+ state protoimpl.MessageState `protogen:"open.v1"`
88+ // Repo is the name of the repository.
89+ Repo string `protobuf:"bytes,1,opt,name=repo,proto3" json:"repo,omitempty"`
90+ // Number identifies the issue within its repository.
91+ Number int64 `protobuf:"varint,2,opt,name=number,proto3" json:"number,omitempty"`
92+ // Title is the one-line title.
93+ Title string `protobuf:"bytes,3,opt,name=title,proto3" json:"title,omitempty"`
94+ // Body is the content of the issue. It may be empty.
95+ Body string `protobuf:"bytes,4,opt,name=body,proto3" json:"body,omitempty"`
96+ // State is where the issue is in its life.
97+ State IssueState `protobuf:"varint,5,opt,name=state,proto3,enum=ermite.IssueState" json:"state,omitempty"`
98+ // Author names who created the issue: the label of the token or session
99+ // that was used ("root" for the root token, "owner" for a web session), or,
100+ // for an issue approved from the public form, the unverified display name
101+ // the visitor typed (or "anonymous"). It is informational and never proves
102+ // identity: labels are not unique, the token may be gone, and a visitor can
103+ // type "owner".
104+ Author string `protobuf:"bytes,6,opt,name=author,proto3" json:"author,omitempty"`
105+ // CreatedAt is when the issue was created.
106+ CreatedAt *timestamppb.Timestamp `protobuf:"bytes,7,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"`
107+ // UpdatedAt is when the issue last changed.
108+ UpdatedAt *timestamppb.Timestamp `protobuf:"bytes,8,opt,name=updated_at,json=updatedAt,proto3" json:"updated_at,omitempty"`
109+ // CommentCount is the number of comments on the issue.
110+ CommentCount int32 `protobuf:"varint,9,opt,name=comment_count,json=commentCount,proto3" json:"comment_count,omitempty"`
111+ unknownFields protoimpl.UnknownFields
112+ sizeCache protoimpl.SizeCache
113+}
114+
115+func (x *Issue) Reset() {
116+ *x = Issue{}
117+ mi := &file_ermite_issue_proto_msgTypes[0]
118+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
119+ ms.StoreMessageInfo(mi)
120+}
121+
122+func (x *Issue) String() string {
123+ return protoimpl.X.MessageStringOf(x)
124+}
125+
126+func (*Issue) ProtoMessage() {}
127+
128+func (x *Issue) ProtoReflect() protoreflect.Message {
129+ mi := &file_ermite_issue_proto_msgTypes[0]
130+ if x != nil {
131+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
132+ if ms.LoadMessageInfo() == nil {
133+ ms.StoreMessageInfo(mi)
134+ }
135+ return ms
136+ }
137+ return mi.MessageOf(x)
138+}
139+
140+// Deprecated: Use Issue.ProtoReflect.Descriptor instead.
141+func (*Issue) Descriptor() ([]byte, []int) {
142+ return file_ermite_issue_proto_rawDescGZIP(), []int{0}
143+}
144+
145+func (x *Issue) GetRepo() string {
146+ if x != nil {
147+ return x.Repo
148+ }
149+ return ""
150+}
151+
152+func (x *Issue) GetNumber() int64 {
153+ if x != nil {
154+ return x.Number
155+ }
156+ return 0
157+}
158+
159+func (x *Issue) GetTitle() string {
160+ if x != nil {
161+ return x.Title
162+ }
163+ return ""
164+}
165+
166+func (x *Issue) GetBody() string {
167+ if x != nil {
168+ return x.Body
169+ }
170+ return ""
171+}
172+
173+func (x *Issue) GetState() IssueState {
174+ if x != nil {
175+ return x.State
176+ }
177+ return IssueState_ISSUE_STATE_UNSPECIFIED
178+}
179+
180+func (x *Issue) GetAuthor() string {
181+ if x != nil {
182+ return x.Author
183+ }
184+ return ""
185+}
186+
187+func (x *Issue) GetCreatedAt() *timestamppb.Timestamp {
188+ if x != nil {
189+ return x.CreatedAt
190+ }
191+ return nil
192+}
193+
194+func (x *Issue) GetUpdatedAt() *timestamppb.Timestamp {
195+ if x != nil {
196+ return x.UpdatedAt
197+ }
198+ return nil
199+}
200+
201+func (x *Issue) GetCommentCount() int32 {
202+ if x != nil {
203+ return x.CommentCount
204+ }
205+ return 0
206+}
207+
208+// IssueComment is a note on an issue.
209+type IssueComment struct {
210+ state protoimpl.MessageState `protogen:"open.v1"`
211+ // Id identifies the comment within its issue. Opaque.
212+ Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
213+ // Author names who wrote the comment, like Issue.author.
214+ Author string `protobuf:"bytes,2,opt,name=author,proto3" json:"author,omitempty"`
215+ // Text is the content of the comment.
216+ Text string `protobuf:"bytes,3,opt,name=text,proto3" json:"text,omitempty"`
217+ // CreatedAt is when the comment was written.
218+ CreatedAt *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"`
219+ unknownFields protoimpl.UnknownFields
220+ sizeCache protoimpl.SizeCache
221+}
222+
223+func (x *IssueComment) Reset() {
224+ *x = IssueComment{}
225+ mi := &file_ermite_issue_proto_msgTypes[1]
226+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
227+ ms.StoreMessageInfo(mi)
228+}
229+
230+func (x *IssueComment) String() string {
231+ return protoimpl.X.MessageStringOf(x)
232+}
233+
234+func (*IssueComment) ProtoMessage() {}
235+
236+func (x *IssueComment) ProtoReflect() protoreflect.Message {
237+ mi := &file_ermite_issue_proto_msgTypes[1]
238+ if x != nil {
239+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
240+ if ms.LoadMessageInfo() == nil {
241+ ms.StoreMessageInfo(mi)
242+ }
243+ return ms
244+ }
245+ return mi.MessageOf(x)
246+}
247+
248+// Deprecated: Use IssueComment.ProtoReflect.Descriptor instead.
249+func (*IssueComment) Descriptor() ([]byte, []int) {
250+ return file_ermite_issue_proto_rawDescGZIP(), []int{1}
251+}
252+
253+func (x *IssueComment) GetId() string {
254+ if x != nil {
255+ return x.Id
256+ }
257+ return ""
258+}
259+
260+func (x *IssueComment) GetAuthor() string {
261+ if x != nil {
262+ return x.Author
263+ }
264+ return ""
265+}
266+
267+func (x *IssueComment) GetText() string {
268+ if x != nil {
269+ return x.Text
270+ }
271+ return ""
272+}
273+
274+func (x *IssueComment) GetCreatedAt() *timestamppb.Timestamp {
275+ if x != nil {
276+ return x.CreatedAt
277+ }
278+ return nil
279+}
280+
281+// CreateIssueRequest describes the issue to open.
282+type CreateIssueRequest struct {
283+ state protoimpl.MessageState `protogen:"open.v1"`
284+ // Repo is the repository name.
285+ Repo string `protobuf:"bytes,1,opt,name=repo,proto3" json:"repo,omitempty"`
286+ // Title is the one-line title. Required.
287+ Title string `protobuf:"bytes,2,opt,name=title,proto3" json:"title,omitempty"`
288+ // Body is the optional content.
289+ Body string `protobuf:"bytes,3,opt,name=body,proto3" json:"body,omitempty"`
290+ unknownFields protoimpl.UnknownFields
291+ sizeCache protoimpl.SizeCache
292+}
293+
294+func (x *CreateIssueRequest) Reset() {
295+ *x = CreateIssueRequest{}
296+ mi := &file_ermite_issue_proto_msgTypes[2]
297+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
298+ ms.StoreMessageInfo(mi)
299+}
300+
301+func (x *CreateIssueRequest) String() string {
302+ return protoimpl.X.MessageStringOf(x)
303+}
304+
305+func (*CreateIssueRequest) ProtoMessage() {}
306+
307+func (x *CreateIssueRequest) ProtoReflect() protoreflect.Message {
308+ mi := &file_ermite_issue_proto_msgTypes[2]
309+ if x != nil {
310+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
311+ if ms.LoadMessageInfo() == nil {
312+ ms.StoreMessageInfo(mi)
313+ }
314+ return ms
315+ }
316+ return mi.MessageOf(x)
317+}
318+
319+// Deprecated: Use CreateIssueRequest.ProtoReflect.Descriptor instead.
320+func (*CreateIssueRequest) Descriptor() ([]byte, []int) {
321+ return file_ermite_issue_proto_rawDescGZIP(), []int{2}
322+}
323+
324+func (x *CreateIssueRequest) GetRepo() string {
325+ if x != nil {
326+ return x.Repo
327+ }
328+ return ""
329+}
330+
331+func (x *CreateIssueRequest) GetTitle() string {
332+ if x != nil {
333+ return x.Title
334+ }
335+ return ""
336+}
337+
338+func (x *CreateIssueRequest) GetBody() string {
339+ if x != nil {
340+ return x.Body
341+ }
342+ return ""
343+}
344+
345+// CreateIssueResponse holds the new issue.
346+type CreateIssueResponse struct {
347+ state protoimpl.MessageState `protogen:"open.v1"`
348+ // Issue is the created issue.
349+ Issue *Issue `protobuf:"bytes,1,opt,name=issue,proto3" json:"issue,omitempty"`
350+ unknownFields protoimpl.UnknownFields
351+ sizeCache protoimpl.SizeCache
352+}
353+
354+func (x *CreateIssueResponse) Reset() {
355+ *x = CreateIssueResponse{}
356+ mi := &file_ermite_issue_proto_msgTypes[3]
357+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
358+ ms.StoreMessageInfo(mi)
359+}
360+
361+func (x *CreateIssueResponse) String() string {
362+ return protoimpl.X.MessageStringOf(x)
363+}
364+
365+func (*CreateIssueResponse) ProtoMessage() {}
366+
367+func (x *CreateIssueResponse) ProtoReflect() protoreflect.Message {
368+ mi := &file_ermite_issue_proto_msgTypes[3]
369+ if x != nil {
370+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
371+ if ms.LoadMessageInfo() == nil {
372+ ms.StoreMessageInfo(mi)
373+ }
374+ return ms
375+ }
376+ return mi.MessageOf(x)
377+}
378+
379+// Deprecated: Use CreateIssueResponse.ProtoReflect.Descriptor instead.
380+func (*CreateIssueResponse) Descriptor() ([]byte, []int) {
381+ return file_ermite_issue_proto_rawDescGZIP(), []int{3}
382+}
383+
384+func (x *CreateIssueResponse) GetIssue() *Issue {
385+ if x != nil {
386+ return x.Issue
387+ }
388+ return nil
389+}
390+
391+// ListIssuesRequest selects a page of issues.
392+type ListIssuesRequest struct {
393+ state protoimpl.MessageState `protogen:"open.v1"`
394+ // Repo is the repository name.
395+ Repo string `protobuf:"bytes,1,opt,name=repo,proto3" json:"repo,omitempty"`
396+ // State restricts the result to one state. ISSUE_STATE_UNSPECIFIED means
397+ // every state.
398+ State IssueState `protobuf:"varint,2,opt,name=state,proto3,enum=ermite.IssueState" json:"state,omitempty"`
399+ // PageSize is the maximum number of issues to return. 0 means the server
400+ // default; values above the server maximum are lowered to it.
401+ PageSize int32 `protobuf:"varint,3,opt,name=page_size,json=pageSize,proto3" json:"page_size,omitempty"`
402+ // PageToken is the next_page_token of a previous response, to continue the
403+ // same listing. Empty for the first page. Other fields must be unchanged.
404+ PageToken string `protobuf:"bytes,4,opt,name=page_token,json=pageToken,proto3" json:"page_token,omitempty"`
405+ unknownFields protoimpl.UnknownFields
406+ sizeCache protoimpl.SizeCache
407+}
408+
409+func (x *ListIssuesRequest) Reset() {
410+ *x = ListIssuesRequest{}
411+ mi := &file_ermite_issue_proto_msgTypes[4]
412+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
413+ ms.StoreMessageInfo(mi)
414+}
415+
416+func (x *ListIssuesRequest) String() string {
417+ return protoimpl.X.MessageStringOf(x)
418+}
419+
420+func (*ListIssuesRequest) ProtoMessage() {}
421+
422+func (x *ListIssuesRequest) ProtoReflect() protoreflect.Message {
423+ mi := &file_ermite_issue_proto_msgTypes[4]
424+ if x != nil {
425+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
426+ if ms.LoadMessageInfo() == nil {
427+ ms.StoreMessageInfo(mi)
428+ }
429+ return ms
430+ }
431+ return mi.MessageOf(x)
432+}
433+
434+// Deprecated: Use ListIssuesRequest.ProtoReflect.Descriptor instead.
435+func (*ListIssuesRequest) Descriptor() ([]byte, []int) {
436+ return file_ermite_issue_proto_rawDescGZIP(), []int{4}
437+}
438+
439+func (x *ListIssuesRequest) GetRepo() string {
440+ if x != nil {
441+ return x.Repo
442+ }
443+ return ""
444+}
445+
446+func (x *ListIssuesRequest) GetState() IssueState {
447+ if x != nil {
448+ return x.State
449+ }
450+ return IssueState_ISSUE_STATE_UNSPECIFIED
451+}
452+
453+func (x *ListIssuesRequest) GetPageSize() int32 {
454+ if x != nil {
455+ return x.PageSize
456+ }
457+ return 0
458+}
459+
460+func (x *ListIssuesRequest) GetPageToken() string {
461+ if x != nil {
462+ return x.PageToken
463+ }
464+ return ""
465+}
466+
467+// ListIssuesResponse is one page of issues.
468+type ListIssuesResponse struct {
469+ state protoimpl.MessageState `protogen:"open.v1"`
470+ // Issues are the issues of this page, highest number first.
471+ Issues []*Issue `protobuf:"bytes,1,rep,name=issues,proto3" json:"issues,omitempty"`
472+ // NextPageToken is passed as page_token to get the next page. Empty when
473+ // there is no next page.
474+ NextPageToken string `protobuf:"bytes,2,opt,name=next_page_token,json=nextPageToken,proto3" json:"next_page_token,omitempty"`
475+ unknownFields protoimpl.UnknownFields
476+ sizeCache protoimpl.SizeCache
477+}
478+
479+func (x *ListIssuesResponse) Reset() {
480+ *x = ListIssuesResponse{}
481+ mi := &file_ermite_issue_proto_msgTypes[5]
482+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
483+ ms.StoreMessageInfo(mi)
484+}
485+
486+func (x *ListIssuesResponse) String() string {
487+ return protoimpl.X.MessageStringOf(x)
488+}
489+
490+func (*ListIssuesResponse) ProtoMessage() {}
491+
492+func (x *ListIssuesResponse) ProtoReflect() protoreflect.Message {
493+ mi := &file_ermite_issue_proto_msgTypes[5]
494+ if x != nil {
495+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
496+ if ms.LoadMessageInfo() == nil {
497+ ms.StoreMessageInfo(mi)
498+ }
499+ return ms
500+ }
501+ return mi.MessageOf(x)
502+}
503+
504+// Deprecated: Use ListIssuesResponse.ProtoReflect.Descriptor instead.
505+func (*ListIssuesResponse) Descriptor() ([]byte, []int) {
506+ return file_ermite_issue_proto_rawDescGZIP(), []int{5}
507+}
508+
509+func (x *ListIssuesResponse) GetIssues() []*Issue {
510+ if x != nil {
511+ return x.Issues
512+ }
513+ return nil
514+}
515+
516+func (x *ListIssuesResponse) GetNextPageToken() string {
517+ if x != nil {
518+ return x.NextPageToken
519+ }
520+ return ""
521+}
522+
523+// GetIssueRequest selects an issue.
524+type GetIssueRequest struct {
525+ state protoimpl.MessageState `protogen:"open.v1"`
526+ // Repo is the repository name.
527+ Repo string `protobuf:"bytes,1,opt,name=repo,proto3" json:"repo,omitempty"`
528+ // Number is the issue number.
529+ Number int64 `protobuf:"varint,2,opt,name=number,proto3" json:"number,omitempty"`
530+ unknownFields protoimpl.UnknownFields
531+ sizeCache protoimpl.SizeCache
532+}
533+
534+func (x *GetIssueRequest) Reset() {
535+ *x = GetIssueRequest{}
536+ mi := &file_ermite_issue_proto_msgTypes[6]
537+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
538+ ms.StoreMessageInfo(mi)
539+}
540+
541+func (x *GetIssueRequest) String() string {
542+ return protoimpl.X.MessageStringOf(x)
543+}
544+
545+func (*GetIssueRequest) ProtoMessage() {}
546+
547+func (x *GetIssueRequest) ProtoReflect() protoreflect.Message {
548+ mi := &file_ermite_issue_proto_msgTypes[6]
549+ if x != nil {
550+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
551+ if ms.LoadMessageInfo() == nil {
552+ ms.StoreMessageInfo(mi)
553+ }
554+ return ms
555+ }
556+ return mi.MessageOf(x)
557+}
558+
559+// Deprecated: Use GetIssueRequest.ProtoReflect.Descriptor instead.
560+func (*GetIssueRequest) Descriptor() ([]byte, []int) {
561+ return file_ermite_issue_proto_rawDescGZIP(), []int{6}
562+}
563+
564+func (x *GetIssueRequest) GetRepo() string {
565+ if x != nil {
566+ return x.Repo
567+ }
568+ return ""
569+}
570+
571+func (x *GetIssueRequest) GetNumber() int64 {
572+ if x != nil {
573+ return x.Number
574+ }
575+ return 0
576+}
577+
578+// GetIssueResponse holds an issue with its comments.
579+type GetIssueResponse struct {
580+ state protoimpl.MessageState `protogen:"open.v1"`
581+ // Issue is the issue.
582+ Issue *Issue `protobuf:"bytes,1,opt,name=issue,proto3" json:"issue,omitempty"`
583+ // Comments are every comment of the issue, oldest first.
584+ Comments []*IssueComment `protobuf:"bytes,2,rep,name=comments,proto3" json:"comments,omitempty"`
585+ unknownFields protoimpl.UnknownFields
586+ sizeCache protoimpl.SizeCache
587+}
588+
589+func (x *GetIssueResponse) Reset() {
590+ *x = GetIssueResponse{}
591+ mi := &file_ermite_issue_proto_msgTypes[7]
592+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
593+ ms.StoreMessageInfo(mi)
594+}
595+
596+func (x *GetIssueResponse) String() string {
597+ return protoimpl.X.MessageStringOf(x)
598+}
599+
600+func (*GetIssueResponse) ProtoMessage() {}
601+
602+func (x *GetIssueResponse) ProtoReflect() protoreflect.Message {
603+ mi := &file_ermite_issue_proto_msgTypes[7]
604+ if x != nil {
605+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
606+ if ms.LoadMessageInfo() == nil {
607+ ms.StoreMessageInfo(mi)
608+ }
609+ return ms
610+ }
611+ return mi.MessageOf(x)
612+}
613+
614+// Deprecated: Use GetIssueResponse.ProtoReflect.Descriptor instead.
615+func (*GetIssueResponse) Descriptor() ([]byte, []int) {
616+ return file_ermite_issue_proto_rawDescGZIP(), []int{7}
617+}
618+
619+func (x *GetIssueResponse) GetIssue() *Issue {
620+ if x != nil {
621+ return x.Issue
622+ }
623+ return nil
624+}
625+
626+func (x *GetIssueResponse) GetComments() []*IssueComment {
627+ if x != nil {
628+ return x.Comments
629+ }
630+ return nil
631+}
632+
633+// AddIssueCommentRequest describes the comment to add.
634+type AddIssueCommentRequest struct {
635+ state protoimpl.MessageState `protogen:"open.v1"`
636+ // Repo is the repository name.
637+ Repo string `protobuf:"bytes,1,opt,name=repo,proto3" json:"repo,omitempty"`
638+ // Number is the issue number.
639+ Number int64 `protobuf:"varint,2,opt,name=number,proto3" json:"number,omitempty"`
640+ // Text is the content. Required.
641+ Text string `protobuf:"bytes,3,opt,name=text,proto3" json:"text,omitempty"`
642+ unknownFields protoimpl.UnknownFields
643+ sizeCache protoimpl.SizeCache
644+}
645+
646+func (x *AddIssueCommentRequest) Reset() {
647+ *x = AddIssueCommentRequest{}
648+ mi := &file_ermite_issue_proto_msgTypes[8]
649+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
650+ ms.StoreMessageInfo(mi)
651+}
652+
653+func (x *AddIssueCommentRequest) String() string {
654+ return protoimpl.X.MessageStringOf(x)
655+}
656+
657+func (*AddIssueCommentRequest) ProtoMessage() {}
658+
659+func (x *AddIssueCommentRequest) ProtoReflect() protoreflect.Message {
660+ mi := &file_ermite_issue_proto_msgTypes[8]
661+ if x != nil {
662+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
663+ if ms.LoadMessageInfo() == nil {
664+ ms.StoreMessageInfo(mi)
665+ }
666+ return ms
667+ }
668+ return mi.MessageOf(x)
669+}
670+
671+// Deprecated: Use AddIssueCommentRequest.ProtoReflect.Descriptor instead.
672+func (*AddIssueCommentRequest) Descriptor() ([]byte, []int) {
673+ return file_ermite_issue_proto_rawDescGZIP(), []int{8}
674+}
675+
676+func (x *AddIssueCommentRequest) GetRepo() string {
677+ if x != nil {
678+ return x.Repo
679+ }
680+ return ""
681+}
682+
683+func (x *AddIssueCommentRequest) GetNumber() int64 {
684+ if x != nil {
685+ return x.Number
686+ }
687+ return 0
688+}
689+
690+func (x *AddIssueCommentRequest) GetText() string {
691+ if x != nil {
692+ return x.Text
693+ }
694+ return ""
695+}
696+
697+// AddIssueCommentResponse holds the new comment.
698+type AddIssueCommentResponse struct {
699+ state protoimpl.MessageState `protogen:"open.v1"`
700+ // Comment is the created comment.
701+ Comment *IssueComment `protobuf:"bytes,1,opt,name=comment,proto3" json:"comment,omitempty"`
702+ unknownFields protoimpl.UnknownFields
703+ sizeCache protoimpl.SizeCache
704+}
705+
706+func (x *AddIssueCommentResponse) Reset() {
707+ *x = AddIssueCommentResponse{}
708+ mi := &file_ermite_issue_proto_msgTypes[9]
709+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
710+ ms.StoreMessageInfo(mi)
711+}
712+
713+func (x *AddIssueCommentResponse) String() string {
714+ return protoimpl.X.MessageStringOf(x)
715+}
716+
717+func (*AddIssueCommentResponse) ProtoMessage() {}
718+
719+func (x *AddIssueCommentResponse) ProtoReflect() protoreflect.Message {
720+ mi := &file_ermite_issue_proto_msgTypes[9]
721+ if x != nil {
722+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
723+ if ms.LoadMessageInfo() == nil {
724+ ms.StoreMessageInfo(mi)
725+ }
726+ return ms
727+ }
728+ return mi.MessageOf(x)
729+}
730+
731+// Deprecated: Use AddIssueCommentResponse.ProtoReflect.Descriptor instead.
732+func (*AddIssueCommentResponse) Descriptor() ([]byte, []int) {
733+ return file_ermite_issue_proto_rawDescGZIP(), []int{9}
734+}
735+
736+func (x *AddIssueCommentResponse) GetComment() *IssueComment {
737+ if x != nil {
738+ return x.Comment
739+ }
740+ return nil
741+}
742+
743+// CloseIssueRequest names the issue to close.
744+type CloseIssueRequest struct {
745+ state protoimpl.MessageState `protogen:"open.v1"`
746+ // Repo is the repository name.
747+ Repo string `protobuf:"bytes,1,opt,name=repo,proto3" json:"repo,omitempty"`
748+ // Number is the issue number.
749+ Number int64 `protobuf:"varint,2,opt,name=number,proto3" json:"number,omitempty"`
750+ unknownFields protoimpl.UnknownFields
751+ sizeCache protoimpl.SizeCache
752+}
753+
754+func (x *CloseIssueRequest) Reset() {
755+ *x = CloseIssueRequest{}
756+ mi := &file_ermite_issue_proto_msgTypes[10]
757+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
758+ ms.StoreMessageInfo(mi)
759+}
760+
761+func (x *CloseIssueRequest) String() string {
762+ return protoimpl.X.MessageStringOf(x)
763+}
764+
765+func (*CloseIssueRequest) ProtoMessage() {}
766+
767+func (x *CloseIssueRequest) ProtoReflect() protoreflect.Message {
768+ mi := &file_ermite_issue_proto_msgTypes[10]
769+ if x != nil {
770+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
771+ if ms.LoadMessageInfo() == nil {
772+ ms.StoreMessageInfo(mi)
773+ }
774+ return ms
775+ }
776+ return mi.MessageOf(x)
777+}
778+
779+// Deprecated: Use CloseIssueRequest.ProtoReflect.Descriptor instead.
780+func (*CloseIssueRequest) Descriptor() ([]byte, []int) {
781+ return file_ermite_issue_proto_rawDescGZIP(), []int{10}
782+}
783+
784+func (x *CloseIssueRequest) GetRepo() string {
785+ if x != nil {
786+ return x.Repo
787+ }
788+ return ""
789+}
790+
791+func (x *CloseIssueRequest) GetNumber() int64 {
792+ if x != nil {
793+ return x.Number
794+ }
795+ return 0
796+}
797+
798+// CloseIssueResponse holds the issue after closing.
799+type CloseIssueResponse struct {
800+ state protoimpl.MessageState `protogen:"open.v1"`
801+ // Issue is the closed issue.
802+ Issue *Issue `protobuf:"bytes,1,opt,name=issue,proto3" json:"issue,omitempty"`
803+ unknownFields protoimpl.UnknownFields
804+ sizeCache protoimpl.SizeCache
805+}
806+
807+func (x *CloseIssueResponse) Reset() {
808+ *x = CloseIssueResponse{}
809+ mi := &file_ermite_issue_proto_msgTypes[11]
810+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
811+ ms.StoreMessageInfo(mi)
812+}
813+
814+func (x *CloseIssueResponse) String() string {
815+ return protoimpl.X.MessageStringOf(x)
816+}
817+
818+func (*CloseIssueResponse) ProtoMessage() {}
819+
820+func (x *CloseIssueResponse) ProtoReflect() protoreflect.Message {
821+ mi := &file_ermite_issue_proto_msgTypes[11]
822+ if x != nil {
823+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
824+ if ms.LoadMessageInfo() == nil {
825+ ms.StoreMessageInfo(mi)
826+ }
827+ return ms
828+ }
829+ return mi.MessageOf(x)
830+}
831+
832+// Deprecated: Use CloseIssueResponse.ProtoReflect.Descriptor instead.
833+func (*CloseIssueResponse) Descriptor() ([]byte, []int) {
834+ return file_ermite_issue_proto_rawDescGZIP(), []int{11}
835+}
836+
837+func (x *CloseIssueResponse) GetIssue() *Issue {
838+ if x != nil {
839+ return x.Issue
840+ }
841+ return nil
842+}
843+
844+// ReopenIssueRequest names the issue to reopen.
845+type ReopenIssueRequest struct {
846+ state protoimpl.MessageState `protogen:"open.v1"`
847+ // Repo is the repository name.
848+ Repo string `protobuf:"bytes,1,opt,name=repo,proto3" json:"repo,omitempty"`
849+ // Number is the issue number.
850+ Number int64 `protobuf:"varint,2,opt,name=number,proto3" json:"number,omitempty"`
851+ unknownFields protoimpl.UnknownFields
852+ sizeCache protoimpl.SizeCache
853+}
854+
855+func (x *ReopenIssueRequest) Reset() {
856+ *x = ReopenIssueRequest{}
857+ mi := &file_ermite_issue_proto_msgTypes[12]
858+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
859+ ms.StoreMessageInfo(mi)
860+}
861+
862+func (x *ReopenIssueRequest) String() string {
863+ return protoimpl.X.MessageStringOf(x)
864+}
865+
866+func (*ReopenIssueRequest) ProtoMessage() {}
867+
868+func (x *ReopenIssueRequest) ProtoReflect() protoreflect.Message {
869+ mi := &file_ermite_issue_proto_msgTypes[12]
870+ if x != nil {
871+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
872+ if ms.LoadMessageInfo() == nil {
873+ ms.StoreMessageInfo(mi)
874+ }
875+ return ms
876+ }
877+ return mi.MessageOf(x)
878+}
879+
880+// Deprecated: Use ReopenIssueRequest.ProtoReflect.Descriptor instead.
881+func (*ReopenIssueRequest) Descriptor() ([]byte, []int) {
882+ return file_ermite_issue_proto_rawDescGZIP(), []int{12}
883+}
884+
885+func (x *ReopenIssueRequest) GetRepo() string {
886+ if x != nil {
887+ return x.Repo
888+ }
889+ return ""
890+}
891+
892+func (x *ReopenIssueRequest) GetNumber() int64 {
893+ if x != nil {
894+ return x.Number
895+ }
896+ return 0
897+}
898+
899+// ReopenIssueResponse holds the issue after reopening.
900+type ReopenIssueResponse struct {
901+ state protoimpl.MessageState `protogen:"open.v1"`
902+ // Issue is the reopened issue.
903+ Issue *Issue `protobuf:"bytes,1,opt,name=issue,proto3" json:"issue,omitempty"`
904+ unknownFields protoimpl.UnknownFields
905+ sizeCache protoimpl.SizeCache
906+}
907+
908+func (x *ReopenIssueResponse) Reset() {
909+ *x = ReopenIssueResponse{}
910+ mi := &file_ermite_issue_proto_msgTypes[13]
911+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
912+ ms.StoreMessageInfo(mi)
913+}
914+
915+func (x *ReopenIssueResponse) String() string {
916+ return protoimpl.X.MessageStringOf(x)
917+}
918+
919+func (*ReopenIssueResponse) ProtoMessage() {}
920+
921+func (x *ReopenIssueResponse) ProtoReflect() protoreflect.Message {
922+ mi := &file_ermite_issue_proto_msgTypes[13]
923+ if x != nil {
924+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
925+ if ms.LoadMessageInfo() == nil {
926+ ms.StoreMessageInfo(mi)
927+ }
928+ return ms
929+ }
930+ return mi.MessageOf(x)
931+}
932+
933+// Deprecated: Use ReopenIssueResponse.ProtoReflect.Descriptor instead.
934+func (*ReopenIssueResponse) Descriptor() ([]byte, []int) {
935+ return file_ermite_issue_proto_rawDescGZIP(), []int{13}
936+}
937+
938+func (x *ReopenIssueResponse) GetIssue() *Issue {
939+ if x != nil {
940+ return x.Issue
941+ }
942+ return nil
943+}
944+
945+// PendingIssue is an issue awaiting moderation. It has no number yet.
946+type PendingIssue struct {
947+ state protoimpl.MessageState `protogen:"open.v1"`
948+ // Id identifies the pending issue. Opaque internal id, not a number.
949+ Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
950+ // Repo is the name of the repository.
951+ Repo string `protobuf:"bytes,2,opt,name=repo,proto3" json:"repo,omitempty"`
952+ // Title is the one-line title.
953+ Title string `protobuf:"bytes,3,opt,name=title,proto3" json:"title,omitempty"`
954+ // Body is the content. It may be empty.
955+ Body string `protobuf:"bytes,4,opt,name=body,proto3" json:"body,omitempty"`
956+ // Author is the optional display name given by the anonymous submitter. It
957+ // may be empty or "anonymous", and is not verified: it never proves
958+ // identity (a visitor can type "owner"). It becomes Issue.author on approval.
959+ Author string `protobuf:"bytes,5,opt,name=author,proto3" json:"author,omitempty"`
960+ // CreatedAt is when the issue was submitted.
961+ CreatedAt *timestamppb.Timestamp `protobuf:"bytes,6,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"`
962+ unknownFields protoimpl.UnknownFields
963+ sizeCache protoimpl.SizeCache
964+}
965+
966+func (x *PendingIssue) Reset() {
967+ *x = PendingIssue{}
968+ mi := &file_ermite_issue_proto_msgTypes[14]
969+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
970+ ms.StoreMessageInfo(mi)
971+}
972+
973+func (x *PendingIssue) String() string {
974+ return protoimpl.X.MessageStringOf(x)
975+}
976+
977+func (*PendingIssue) ProtoMessage() {}
978+
979+func (x *PendingIssue) ProtoReflect() protoreflect.Message {
980+ mi := &file_ermite_issue_proto_msgTypes[14]
981+ if x != nil {
982+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
983+ if ms.LoadMessageInfo() == nil {
984+ ms.StoreMessageInfo(mi)
985+ }
986+ return ms
987+ }
988+ return mi.MessageOf(x)
989+}
990+
991+// Deprecated: Use PendingIssue.ProtoReflect.Descriptor instead.
992+func (*PendingIssue) Descriptor() ([]byte, []int) {
993+ return file_ermite_issue_proto_rawDescGZIP(), []int{14}
994+}
995+
996+func (x *PendingIssue) GetId() string {
997+ if x != nil {
998+ return x.Id
999+ }
1000+ return ""
1001+}
1002+
1003+func (x *PendingIssue) GetRepo() string {
1004+ if x != nil {
1005+ return x.Repo
1006+ }
1007+ return ""
1008+}
1009+
1010+func (x *PendingIssue) GetTitle() string {
1011+ if x != nil {
1012+ return x.Title
1013+ }
1014+ return ""
1015+}
1016+
1017+func (x *PendingIssue) GetBody() string {
1018+ if x != nil {
1019+ return x.Body
1020+ }
1021+ return ""
1022+}
1023+
1024+func (x *PendingIssue) GetAuthor() string {
1025+ if x != nil {
1026+ return x.Author
1027+ }
1028+ return ""
1029+}
1030+
1031+func (x *PendingIssue) GetCreatedAt() *timestamppb.Timestamp {
1032+ if x != nil {
1033+ return x.CreatedAt
1034+ }
1035+ return nil
1036+}
1037+
1038+// ListPendingIssuesRequest selects a page of pending issues.
1039+type ListPendingIssuesRequest struct {
1040+ state protoimpl.MessageState `protogen:"open.v1"`
1041+ // Repo is the repository name. Empty means every repository.
1042+ Repo string `protobuf:"bytes,1,opt,name=repo,proto3" json:"repo,omitempty"`
1043+ // PageSize is the maximum number of issues to return. 0 means the server
1044+ // default; values above the server maximum are lowered to it.
1045+ PageSize int32 `protobuf:"varint,2,opt,name=page_size,json=pageSize,proto3" json:"page_size,omitempty"`
1046+ // PageToken is the next_page_token of a previous response, to continue the
1047+ // same listing. Empty for the first page. Other fields must be unchanged.
1048+ PageToken string `protobuf:"bytes,3,opt,name=page_token,json=pageToken,proto3" json:"page_token,omitempty"`
1049+ unknownFields protoimpl.UnknownFields
1050+ sizeCache protoimpl.SizeCache
1051+}
1052+
1053+func (x *ListPendingIssuesRequest) Reset() {
1054+ *x = ListPendingIssuesRequest{}
1055+ mi := &file_ermite_issue_proto_msgTypes[15]
1056+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
1057+ ms.StoreMessageInfo(mi)
1058+}
1059+
1060+func (x *ListPendingIssuesRequest) String() string {
1061+ return protoimpl.X.MessageStringOf(x)
1062+}
1063+
1064+func (*ListPendingIssuesRequest) ProtoMessage() {}
1065+
1066+func (x *ListPendingIssuesRequest) ProtoReflect() protoreflect.Message {
1067+ mi := &file_ermite_issue_proto_msgTypes[15]
1068+ if x != nil {
1069+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
1070+ if ms.LoadMessageInfo() == nil {
1071+ ms.StoreMessageInfo(mi)
1072+ }
1073+ return ms
1074+ }
1075+ return mi.MessageOf(x)
1076+}
1077+
1078+// Deprecated: Use ListPendingIssuesRequest.ProtoReflect.Descriptor instead.
1079+func (*ListPendingIssuesRequest) Descriptor() ([]byte, []int) {
1080+ return file_ermite_issue_proto_rawDescGZIP(), []int{15}
1081+}
1082+
1083+func (x *ListPendingIssuesRequest) GetRepo() string {
1084+ if x != nil {
1085+ return x.Repo
1086+ }
1087+ return ""
1088+}
1089+
1090+func (x *ListPendingIssuesRequest) GetPageSize() int32 {
1091+ if x != nil {
1092+ return x.PageSize
1093+ }
1094+ return 0
1095+}
1096+
1097+func (x *ListPendingIssuesRequest) GetPageToken() string {
1098+ if x != nil {
1099+ return x.PageToken
1100+ }
1101+ return ""
1102+}
1103+
1104+// ListPendingIssuesResponse is one page of pending issues.
1105+type ListPendingIssuesResponse struct {
1106+ state protoimpl.MessageState `protogen:"open.v1"`
1107+ // Issues are the pending issues of this page, oldest first.
1108+ Issues []*PendingIssue `protobuf:"bytes,1,rep,name=issues,proto3" json:"issues,omitempty"`
1109+ // NextPageToken is passed as page_token to get the next page. Empty when
1110+ // there is no next page.
1111+ NextPageToken string `protobuf:"bytes,2,opt,name=next_page_token,json=nextPageToken,proto3" json:"next_page_token,omitempty"`
1112+ unknownFields protoimpl.UnknownFields
1113+ sizeCache protoimpl.SizeCache
1114+}
1115+
1116+func (x *ListPendingIssuesResponse) Reset() {
1117+ *x = ListPendingIssuesResponse{}
1118+ mi := &file_ermite_issue_proto_msgTypes[16]
1119+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
1120+ ms.StoreMessageInfo(mi)
1121+}
1122+
1123+func (x *ListPendingIssuesResponse) String() string {
1124+ return protoimpl.X.MessageStringOf(x)
1125+}
1126+
1127+func (*ListPendingIssuesResponse) ProtoMessage() {}
1128+
1129+func (x *ListPendingIssuesResponse) ProtoReflect() protoreflect.Message {
1130+ mi := &file_ermite_issue_proto_msgTypes[16]
1131+ if x != nil {
1132+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
1133+ if ms.LoadMessageInfo() == nil {
1134+ ms.StoreMessageInfo(mi)
1135+ }
1136+ return ms
1137+ }
1138+ return mi.MessageOf(x)
1139+}
1140+
1141+// Deprecated: Use ListPendingIssuesResponse.ProtoReflect.Descriptor instead.
1142+func (*ListPendingIssuesResponse) Descriptor() ([]byte, []int) {
1143+ return file_ermite_issue_proto_rawDescGZIP(), []int{16}
1144+}
1145+
1146+func (x *ListPendingIssuesResponse) GetIssues() []*PendingIssue {
1147+ if x != nil {
1148+ return x.Issues
1149+ }
1150+ return nil
1151+}
1152+
1153+func (x *ListPendingIssuesResponse) GetNextPageToken() string {
1154+ if x != nil {
1155+ return x.NextPageToken
1156+ }
1157+ return ""
1158+}
1159+
1160+// ApproveIssueRequest names the pending issue to approve.
1161+type ApproveIssueRequest struct {
1162+ state protoimpl.MessageState `protogen:"open.v1"`
1163+ // Repo is the repository name.
1164+ Repo string `protobuf:"bytes,1,opt,name=repo,proto3" json:"repo,omitempty"`
1165+ // Id is the PendingIssue.id.
1166+ Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"`
1167+ unknownFields protoimpl.UnknownFields
1168+ sizeCache protoimpl.SizeCache
1169+}
1170+
1171+func (x *ApproveIssueRequest) Reset() {
1172+ *x = ApproveIssueRequest{}
1173+ mi := &file_ermite_issue_proto_msgTypes[17]
1174+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
1175+ ms.StoreMessageInfo(mi)
1176+}
1177+
1178+func (x *ApproveIssueRequest) String() string {
1179+ return protoimpl.X.MessageStringOf(x)
1180+}
1181+
1182+func (*ApproveIssueRequest) ProtoMessage() {}
1183+
1184+func (x *ApproveIssueRequest) ProtoReflect() protoreflect.Message {
1185+ mi := &file_ermite_issue_proto_msgTypes[17]
1186+ if x != nil {
1187+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
1188+ if ms.LoadMessageInfo() == nil {
1189+ ms.StoreMessageInfo(mi)
1190+ }
1191+ return ms
1192+ }
1193+ return mi.MessageOf(x)
1194+}
1195+
1196+// Deprecated: Use ApproveIssueRequest.ProtoReflect.Descriptor instead.
1197+func (*ApproveIssueRequest) Descriptor() ([]byte, []int) {
1198+ return file_ermite_issue_proto_rawDescGZIP(), []int{17}
1199+}
1200+
1201+func (x *ApproveIssueRequest) GetRepo() string {
1202+ if x != nil {
1203+ return x.Repo
1204+ }
1205+ return ""
1206+}
1207+
1208+func (x *ApproveIssueRequest) GetId() string {
1209+ if x != nil {
1210+ return x.Id
1211+ }
1212+ return ""
1213+}
1214+
1215+// ApproveIssueResponse holds the approved issue.
1216+type ApproveIssueResponse struct {
1217+ state protoimpl.MessageState `protogen:"open.v1"`
1218+ // Issue is the approved issue, with its new number and state
1219+ // ISSUE_STATE_OPEN.
1220+ Issue *Issue `protobuf:"bytes,1,opt,name=issue,proto3" json:"issue,omitempty"`
1221+ unknownFields protoimpl.UnknownFields
1222+ sizeCache protoimpl.SizeCache
1223+}
1224+
1225+func (x *ApproveIssueResponse) Reset() {
1226+ *x = ApproveIssueResponse{}
1227+ mi := &file_ermite_issue_proto_msgTypes[18]
1228+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
1229+ ms.StoreMessageInfo(mi)
1230+}
1231+
1232+func (x *ApproveIssueResponse) String() string {
1233+ return protoimpl.X.MessageStringOf(x)
1234+}
1235+
1236+func (*ApproveIssueResponse) ProtoMessage() {}
1237+
1238+func (x *ApproveIssueResponse) ProtoReflect() protoreflect.Message {
1239+ mi := &file_ermite_issue_proto_msgTypes[18]
1240+ if x != nil {
1241+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
1242+ if ms.LoadMessageInfo() == nil {
1243+ ms.StoreMessageInfo(mi)
1244+ }
1245+ return ms
1246+ }
1247+ return mi.MessageOf(x)
1248+}
1249+
1250+// Deprecated: Use ApproveIssueResponse.ProtoReflect.Descriptor instead.
1251+func (*ApproveIssueResponse) Descriptor() ([]byte, []int) {
1252+ return file_ermite_issue_proto_rawDescGZIP(), []int{18}
1253+}
1254+
1255+func (x *ApproveIssueResponse) GetIssue() *Issue {
1256+ if x != nil {
1257+ return x.Issue
1258+ }
1259+ return nil
1260+}
1261+
1262+// RejectIssueRequest names the pending issue to reject.
1263+type RejectIssueRequest struct {
1264+ state protoimpl.MessageState `protogen:"open.v1"`
1265+ // Repo is the repository name.
1266+ Repo string `protobuf:"bytes,1,opt,name=repo,proto3" json:"repo,omitempty"`
1267+ // Id is the PendingIssue.id.
1268+ Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"`
1269+ // Ban also bans the submitter for a while.
1270+ Ban bool `protobuf:"varint,3,opt,name=ban,proto3" json:"ban,omitempty"`
1271+ unknownFields protoimpl.UnknownFields
1272+ sizeCache protoimpl.SizeCache
1273+}
1274+
1275+func (x *RejectIssueRequest) Reset() {
1276+ *x = RejectIssueRequest{}
1277+ mi := &file_ermite_issue_proto_msgTypes[19]
1278+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
1279+ ms.StoreMessageInfo(mi)
1280+}
1281+
1282+func (x *RejectIssueRequest) String() string {
1283+ return protoimpl.X.MessageStringOf(x)
1284+}
1285+
1286+func (*RejectIssueRequest) ProtoMessage() {}
1287+
1288+func (x *RejectIssueRequest) ProtoReflect() protoreflect.Message {
1289+ mi := &file_ermite_issue_proto_msgTypes[19]
1290+ if x != nil {
1291+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
1292+ if ms.LoadMessageInfo() == nil {
1293+ ms.StoreMessageInfo(mi)
1294+ }
1295+ return ms
1296+ }
1297+ return mi.MessageOf(x)
1298+}
1299+
1300+// Deprecated: Use RejectIssueRequest.ProtoReflect.Descriptor instead.
1301+func (*RejectIssueRequest) Descriptor() ([]byte, []int) {
1302+ return file_ermite_issue_proto_rawDescGZIP(), []int{19}
1303+}
1304+
1305+func (x *RejectIssueRequest) GetRepo() string {
1306+ if x != nil {
1307+ return x.Repo
1308+ }
1309+ return ""
1310+}
1311+
1312+func (x *RejectIssueRequest) GetId() string {
1313+ if x != nil {
1314+ return x.Id
1315+ }
1316+ return ""
1317+}
1318+
1319+func (x *RejectIssueRequest) GetBan() bool {
1320+ if x != nil {
1321+ return x.Ban
1322+ }
1323+ return false
1324+}
1325+
1326+// RejectIssueResponse is empty: the pending issue is gone.
1327+type RejectIssueResponse struct {
1328+ state protoimpl.MessageState `protogen:"open.v1"`
1329+ unknownFields protoimpl.UnknownFields
1330+ sizeCache protoimpl.SizeCache
1331+}
1332+
1333+func (x *RejectIssueResponse) Reset() {
1334+ *x = RejectIssueResponse{}
1335+ mi := &file_ermite_issue_proto_msgTypes[20]
1336+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
1337+ ms.StoreMessageInfo(mi)
1338+}
1339+
1340+func (x *RejectIssueResponse) String() string {
1341+ return protoimpl.X.MessageStringOf(x)
1342+}
1343+
1344+func (*RejectIssueResponse) ProtoMessage() {}
1345+
1346+func (x *RejectIssueResponse) ProtoReflect() protoreflect.Message {
1347+ mi := &file_ermite_issue_proto_msgTypes[20]
1348+ if x != nil {
1349+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
1350+ if ms.LoadMessageInfo() == nil {
1351+ ms.StoreMessageInfo(mi)
1352+ }
1353+ return ms
1354+ }
1355+ return mi.MessageOf(x)
1356+}
1357+
1358+// Deprecated: Use RejectIssueResponse.ProtoReflect.Descriptor instead.
1359+func (*RejectIssueResponse) Descriptor() ([]byte, []int) {
1360+ return file_ermite_issue_proto_rawDescGZIP(), []int{20}
1361+}
1362+
1363+var File_ermite_issue_proto protoreflect.FileDescriptor
1364+
1365+const file_ermite_issue_proto_rawDesc = "" +
1366+ "\n" +
1367+ "\x12ermite/issue.proto\x12\x06ermite\x1a\x1fgoogle/protobuf/timestamp.proto\"\xba\x02\n" +
1368+ "\x05Issue\x12\x12\n" +
1369+ "\x04repo\x18\x01 \x01(\tR\x04repo\x12\x16\n" +
1370+ "\x06number\x18\x02 \x01(\x03R\x06number\x12\x14\n" +
1371+ "\x05title\x18\x03 \x01(\tR\x05title\x12\x12\n" +
1372+ "\x04body\x18\x04 \x01(\tR\x04body\x12(\n" +
1373+ "\x05state\x18\x05 \x01(\x0e2\x12.ermite.IssueStateR\x05state\x12\x16\n" +
1374+ "\x06author\x18\x06 \x01(\tR\x06author\x129\n" +
1375+ "\n" +
1376+ "created_at\x18\a \x01(\v2\x1a.google.protobuf.TimestampR\tcreatedAt\x129\n" +
1377+ "\n" +
1378+ "updated_at\x18\b \x01(\v2\x1a.google.protobuf.TimestampR\tupdatedAt\x12#\n" +
1379+ "\rcomment_count\x18\t \x01(\x05R\fcommentCount\"\x85\x01\n" +
1380+ "\fIssueComment\x12\x0e\n" +
1381+ "\x02id\x18\x01 \x01(\tR\x02id\x12\x16\n" +
1382+ "\x06author\x18\x02 \x01(\tR\x06author\x12\x12\n" +
1383+ "\x04text\x18\x03 \x01(\tR\x04text\x129\n" +
1384+ "\n" +
1385+ "created_at\x18\x04 \x01(\v2\x1a.google.protobuf.TimestampR\tcreatedAt\"R\n" +
1386+ "\x12CreateIssueRequest\x12\x12\n" +
1387+ "\x04repo\x18\x01 \x01(\tR\x04repo\x12\x14\n" +
1388+ "\x05title\x18\x02 \x01(\tR\x05title\x12\x12\n" +
1389+ "\x04body\x18\x03 \x01(\tR\x04body\":\n" +
1390+ "\x13CreateIssueResponse\x12#\n" +
1391+ "\x05issue\x18\x01 \x01(\v2\r.ermite.IssueR\x05issue\"\x8d\x01\n" +
1392+ "\x11ListIssuesRequest\x12\x12\n" +
1393+ "\x04repo\x18\x01 \x01(\tR\x04repo\x12(\n" +
1394+ "\x05state\x18\x02 \x01(\x0e2\x12.ermite.IssueStateR\x05state\x12\x1b\n" +
1395+ "\tpage_size\x18\x03 \x01(\x05R\bpageSize\x12\x1d\n" +
1396+ "\n" +
1397+ "page_token\x18\x04 \x01(\tR\tpageToken\"c\n" +
1398+ "\x12ListIssuesResponse\x12%\n" +
1399+ "\x06issues\x18\x01 \x03(\v2\r.ermite.IssueR\x06issues\x12&\n" +
1400+ "\x0fnext_page_token\x18\x02 \x01(\tR\rnextPageToken\"=\n" +
1401+ "\x0fGetIssueRequest\x12\x12\n" +
1402+ "\x04repo\x18\x01 \x01(\tR\x04repo\x12\x16\n" +
1403+ "\x06number\x18\x02 \x01(\x03R\x06number\"i\n" +
1404+ "\x10GetIssueResponse\x12#\n" +
1405+ "\x05issue\x18\x01 \x01(\v2\r.ermite.IssueR\x05issue\x120\n" +
1406+ "\bcomments\x18\x02 \x03(\v2\x14.ermite.IssueCommentR\bcomments\"X\n" +
1407+ "\x16AddIssueCommentRequest\x12\x12\n" +
1408+ "\x04repo\x18\x01 \x01(\tR\x04repo\x12\x16\n" +
1409+ "\x06number\x18\x02 \x01(\x03R\x06number\x12\x12\n" +
1410+ "\x04text\x18\x03 \x01(\tR\x04text\"I\n" +
1411+ "\x17AddIssueCommentResponse\x12.\n" +
1412+ "\acomment\x18\x01 \x01(\v2\x14.ermite.IssueCommentR\acomment\"?\n" +
1413+ "\x11CloseIssueRequest\x12\x12\n" +
1414+ "\x04repo\x18\x01 \x01(\tR\x04repo\x12\x16\n" +
1415+ "\x06number\x18\x02 \x01(\x03R\x06number\"9\n" +
1416+ "\x12CloseIssueResponse\x12#\n" +
1417+ "\x05issue\x18\x01 \x01(\v2\r.ermite.IssueR\x05issue\"@\n" +
1418+ "\x12ReopenIssueRequest\x12\x12\n" +
1419+ "\x04repo\x18\x01 \x01(\tR\x04repo\x12\x16\n" +
1420+ "\x06number\x18\x02 \x01(\x03R\x06number\":\n" +
1421+ "\x13ReopenIssueResponse\x12#\n" +
1422+ "\x05issue\x18\x01 \x01(\v2\r.ermite.IssueR\x05issue\"\xaf\x01\n" +
1423+ "\fPendingIssue\x12\x0e\n" +
1424+ "\x02id\x18\x01 \x01(\tR\x02id\x12\x12\n" +
1425+ "\x04repo\x18\x02 \x01(\tR\x04repo\x12\x14\n" +
1426+ "\x05title\x18\x03 \x01(\tR\x05title\x12\x12\n" +
1427+ "\x04body\x18\x04 \x01(\tR\x04body\x12\x16\n" +
1428+ "\x06author\x18\x05 \x01(\tR\x06author\x129\n" +
1429+ "\n" +
1430+ "created_at\x18\x06 \x01(\v2\x1a.google.protobuf.TimestampR\tcreatedAt\"j\n" +
1431+ "\x18ListPendingIssuesRequest\x12\x12\n" +
1432+ "\x04repo\x18\x01 \x01(\tR\x04repo\x12\x1b\n" +
1433+ "\tpage_size\x18\x02 \x01(\x05R\bpageSize\x12\x1d\n" +
1434+ "\n" +
1435+ "page_token\x18\x03 \x01(\tR\tpageToken\"q\n" +
1436+ "\x19ListPendingIssuesResponse\x12,\n" +
1437+ "\x06issues\x18\x01 \x03(\v2\x14.ermite.PendingIssueR\x06issues\x12&\n" +
1438+ "\x0fnext_page_token\x18\x02 \x01(\tR\rnextPageToken\"9\n" +
1439+ "\x13ApproveIssueRequest\x12\x12\n" +
1440+ "\x04repo\x18\x01 \x01(\tR\x04repo\x12\x0e\n" +
1441+ "\x02id\x18\x02 \x01(\tR\x02id\";\n" +
1442+ "\x14ApproveIssueResponse\x12#\n" +
1443+ "\x05issue\x18\x01 \x01(\v2\r.ermite.IssueR\x05issue\"J\n" +
1444+ "\x12RejectIssueRequest\x12\x12\n" +
1445+ "\x04repo\x18\x01 \x01(\tR\x04repo\x12\x0e\n" +
1446+ "\x02id\x18\x02 \x01(\tR\x02id\x12\x10\n" +
1447+ "\x03ban\x18\x03 \x01(\bR\x03ban\"\x15\n" +
1448+ "\x13RejectIssueResponse*p\n" +
1449+ "\n" +
1450+ "IssueState\x12\x1b\n" +
1451+ "\x17ISSUE_STATE_UNSPECIFIED\x10\x00\x12\x14\n" +
1452+ "\x10ISSUE_STATE_OPEN\x10\x01\x12\x16\n" +
1453+ "\x12ISSUE_STATE_CLOSED\x10\x02\x12\x17\n" +
1454+ "\x13ISSUE_STATE_PENDING\x10\x032\xa8\x05\n" +
1455+ "\fIssueService\x12F\n" +
1456+ "\vCreateIssue\x12\x1a.ermite.CreateIssueRequest\x1a\x1b.ermite.CreateIssueResponse\x12C\n" +
1457+ "\n" +
1458+ "ListIssues\x12\x19.ermite.ListIssuesRequest\x1a\x1a.ermite.ListIssuesResponse\x12=\n" +
1459+ "\bGetIssue\x12\x17.ermite.GetIssueRequest\x1a\x18.ermite.GetIssueResponse\x12R\n" +
1460+ "\x0fAddIssueComment\x12\x1e.ermite.AddIssueCommentRequest\x1a\x1f.ermite.AddIssueCommentResponse\x12C\n" +
1461+ "\n" +
1462+ "CloseIssue\x12\x19.ermite.CloseIssueRequest\x1a\x1a.ermite.CloseIssueResponse\x12F\n" +
1463+ "\vReopenIssue\x12\x1a.ermite.ReopenIssueRequest\x1a\x1b.ermite.ReopenIssueResponse\x12X\n" +
1464+ "\x11ListPendingIssues\x12 .ermite.ListPendingIssuesRequest\x1a!.ermite.ListPendingIssuesResponse\x12I\n" +
1465+ "\fApproveIssue\x12\x1b.ermite.ApproveIssueRequest\x1a\x1c.ermite.ApproveIssueResponse\x12F\n" +
1466+ "\vRejectIssue\x12\x1a.ermite.RejectIssueRequest\x1a\x1b.ermite.RejectIssueResponseBq\n" +
1467+ "\n" +
1468+ "com.ermiteB\n" +
1469+ "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"
1470+
1471+var (
1472+ file_ermite_issue_proto_rawDescOnce sync.Once
1473+ file_ermite_issue_proto_rawDescData []byte
1474+)
1475+
1476+func file_ermite_issue_proto_rawDescGZIP() []byte {
1477+ file_ermite_issue_proto_rawDescOnce.Do(func() {
1478+ file_ermite_issue_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_ermite_issue_proto_rawDesc), len(file_ermite_issue_proto_rawDesc)))
1479+ })
1480+ return file_ermite_issue_proto_rawDescData
1481+}
1482+
1483+var file_ermite_issue_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
1484+var file_ermite_issue_proto_msgTypes = make([]protoimpl.MessageInfo, 21)
1485+var file_ermite_issue_proto_goTypes = []any{
1486+ (IssueState)(0), // 0: ermite.IssueState
1487+ (*Issue)(nil), // 1: ermite.Issue
1488+ (*IssueComment)(nil), // 2: ermite.IssueComment
1489+ (*CreateIssueRequest)(nil), // 3: ermite.CreateIssueRequest
1490+ (*CreateIssueResponse)(nil), // 4: ermite.CreateIssueResponse
1491+ (*ListIssuesRequest)(nil), // 5: ermite.ListIssuesRequest
1492+ (*ListIssuesResponse)(nil), // 6: ermite.ListIssuesResponse
1493+ (*GetIssueRequest)(nil), // 7: ermite.GetIssueRequest
1494+ (*GetIssueResponse)(nil), // 8: ermite.GetIssueResponse
1495+ (*AddIssueCommentRequest)(nil), // 9: ermite.AddIssueCommentRequest
1496+ (*AddIssueCommentResponse)(nil), // 10: ermite.AddIssueCommentResponse
1497+ (*CloseIssueRequest)(nil), // 11: ermite.CloseIssueRequest
1498+ (*CloseIssueResponse)(nil), // 12: ermite.CloseIssueResponse
1499+ (*ReopenIssueRequest)(nil), // 13: ermite.ReopenIssueRequest
1500+ (*ReopenIssueResponse)(nil), // 14: ermite.ReopenIssueResponse
1501+ (*PendingIssue)(nil), // 15: ermite.PendingIssue
1502+ (*ListPendingIssuesRequest)(nil), // 16: ermite.ListPendingIssuesRequest
1503+ (*ListPendingIssuesResponse)(nil), // 17: ermite.ListPendingIssuesResponse
1504+ (*ApproveIssueRequest)(nil), // 18: ermite.ApproveIssueRequest
1505+ (*ApproveIssueResponse)(nil), // 19: ermite.ApproveIssueResponse
1506+ (*RejectIssueRequest)(nil), // 20: ermite.RejectIssueRequest
1507+ (*RejectIssueResponse)(nil), // 21: ermite.RejectIssueResponse
1508+ (*timestamppb.Timestamp)(nil), // 22: google.protobuf.Timestamp
1509+}
1510+var file_ermite_issue_proto_depIdxs = []int32{
1511+ 0, // 0: ermite.Issue.state:type_name -> ermite.IssueState
1512+ 22, // 1: ermite.Issue.created_at:type_name -> google.protobuf.Timestamp
1513+ 22, // 2: ermite.Issue.updated_at:type_name -> google.protobuf.Timestamp
1514+ 22, // 3: ermite.IssueComment.created_at:type_name -> google.protobuf.Timestamp
1515+ 1, // 4: ermite.CreateIssueResponse.issue:type_name -> ermite.Issue
1516+ 0, // 5: ermite.ListIssuesRequest.state:type_name -> ermite.IssueState
1517+ 1, // 6: ermite.ListIssuesResponse.issues:type_name -> ermite.Issue
1518+ 1, // 7: ermite.GetIssueResponse.issue:type_name -> ermite.Issue
1519+ 2, // 8: ermite.GetIssueResponse.comments:type_name -> ermite.IssueComment
1520+ 2, // 9: ermite.AddIssueCommentResponse.comment:type_name -> ermite.IssueComment
1521+ 1, // 10: ermite.CloseIssueResponse.issue:type_name -> ermite.Issue
1522+ 1, // 11: ermite.ReopenIssueResponse.issue:type_name -> ermite.Issue
1523+ 22, // 12: ermite.PendingIssue.created_at:type_name -> google.protobuf.Timestamp
1524+ 15, // 13: ermite.ListPendingIssuesResponse.issues:type_name -> ermite.PendingIssue
1525+ 1, // 14: ermite.ApproveIssueResponse.issue:type_name -> ermite.Issue
1526+ 3, // 15: ermite.IssueService.CreateIssue:input_type -> ermite.CreateIssueRequest
1527+ 5, // 16: ermite.IssueService.ListIssues:input_type -> ermite.ListIssuesRequest
1528+ 7, // 17: ermite.IssueService.GetIssue:input_type -> ermite.GetIssueRequest
1529+ 9, // 18: ermite.IssueService.AddIssueComment:input_type -> ermite.AddIssueCommentRequest
1530+ 11, // 19: ermite.IssueService.CloseIssue:input_type -> ermite.CloseIssueRequest
1531+ 13, // 20: ermite.IssueService.ReopenIssue:input_type -> ermite.ReopenIssueRequest
1532+ 16, // 21: ermite.IssueService.ListPendingIssues:input_type -> ermite.ListPendingIssuesRequest
1533+ 18, // 22: ermite.IssueService.ApproveIssue:input_type -> ermite.ApproveIssueRequest
1534+ 20, // 23: ermite.IssueService.RejectIssue:input_type -> ermite.RejectIssueRequest
1535+ 4, // 24: ermite.IssueService.CreateIssue:output_type -> ermite.CreateIssueResponse
1536+ 6, // 25: ermite.IssueService.ListIssues:output_type -> ermite.ListIssuesResponse
1537+ 8, // 26: ermite.IssueService.GetIssue:output_type -> ermite.GetIssueResponse
1538+ 10, // 27: ermite.IssueService.AddIssueComment:output_type -> ermite.AddIssueCommentResponse
1539+ 12, // 28: ermite.IssueService.CloseIssue:output_type -> ermite.CloseIssueResponse
1540+ 14, // 29: ermite.IssueService.ReopenIssue:output_type -> ermite.ReopenIssueResponse
1541+ 17, // 30: ermite.IssueService.ListPendingIssues:output_type -> ermite.ListPendingIssuesResponse
1542+ 19, // 31: ermite.IssueService.ApproveIssue:output_type -> ermite.ApproveIssueResponse
1543+ 21, // 32: ermite.IssueService.RejectIssue:output_type -> ermite.RejectIssueResponse
1544+ 24, // [24:33] is the sub-list for method output_type
1545+ 15, // [15:24] is the sub-list for method input_type
1546+ 15, // [15:15] is the sub-list for extension type_name
1547+ 15, // [15:15] is the sub-list for extension extendee
1548+ 0, // [0:15] is the sub-list for field type_name
1549+}
1550+
1551+func init() { file_ermite_issue_proto_init() }
1552+func file_ermite_issue_proto_init() {
1553+ if File_ermite_issue_proto != nil {
1554+ return
1555+ }
1556+ type x struct{}
1557+ out := protoimpl.TypeBuilder{
1558+ File: protoimpl.DescBuilder{
1559+ GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
1560+ RawDescriptor: unsafe.Slice(unsafe.StringData(file_ermite_issue_proto_rawDesc), len(file_ermite_issue_proto_rawDesc)),
1561+ NumEnums: 1,
1562+ NumMessages: 21,
1563+ NumExtensions: 0,
1564+ NumServices: 1,
1565+ },
1566+ GoTypes: file_ermite_issue_proto_goTypes,
1567+ DependencyIndexes: file_ermite_issue_proto_depIdxs,
1568+ EnumInfos: file_ermite_issue_proto_enumTypes,
1569+ MessageInfos: file_ermite_issue_proto_msgTypes,
1570+ }.Build()
1571+ File_ermite_issue_proto = out.File
1572+ file_ermite_issue_proto_goTypes = nil
1573+ file_ermite_issue_proto_depIdxs = nil
1574+}
@@ -0,0 +1,1574 @@
1+// SPDX-License-Identifier: MIT
2+
3+// Code generated by protoc-gen-go. DO NOT EDIT.
4+// versions:
5+// protoc-gen-go v1.36.12
6+// protoc (unknown)
7+// source: ermite/issue.proto
8+
9+package ermite
10+
11+import (
12+ protoreflect "google.golang.org/protobuf/reflect/protoreflect"
13+ protoimpl "google.golang.org/protobuf/runtime/protoimpl"
14+ timestamppb "google.golang.org/protobuf/types/known/timestamppb"
15+ reflect "reflect"
16+ sync "sync"
17+ unsafe "unsafe"
18+)
19+
20+const (
21+ // Verify that this generated code is sufficiently up-to-date.
22+ _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
23+ // Verify that runtime/protoimpl is sufficiently up-to-date.
24+ _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
25+)
26+
27+// IssueState is where an issue is in its life.
28+type IssueState int32
29+
30+const (
31+ // Not set. In ListIssuesRequest.state it means every state. Never returned
32+ // by the server.
33+ IssueState_ISSUE_STATE_UNSPECIFIED IssueState = 0
34+ // The issue is open.
35+ IssueState_ISSUE_STATE_OPEN IssueState = 1
36+ // The issue is closed.
37+ IssueState_ISSUE_STATE_CLOSED IssueState = 2
38+ // Reserved for later use: awaiting moderation, owner-only.
39+ IssueState_ISSUE_STATE_PENDING IssueState = 3
40+)
41+
42+// Enum value maps for IssueState.
43+var (
44+ IssueState_name = map[int32]string{
45+ 0: "ISSUE_STATE_UNSPECIFIED",
46+ 1: "ISSUE_STATE_OPEN",
47+ 2: "ISSUE_STATE_CLOSED",
48+ 3: "ISSUE_STATE_PENDING",
49+ }
50+ IssueState_value = map[string]int32{
51+ "ISSUE_STATE_UNSPECIFIED": 0,
52+ "ISSUE_STATE_OPEN": 1,
53+ "ISSUE_STATE_CLOSED": 2,
54+ "ISSUE_STATE_PENDING": 3,
55+ }
56+)
57+
58+func (x IssueState) Enum() *IssueState {
59+ p := new(IssueState)
60+ *p = x
61+ return p
62+}
63+
64+func (x IssueState) String() string {
65+ return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
66+}
67+
68+func (IssueState) Descriptor() protoreflect.EnumDescriptor {
69+ return file_ermite_issue_proto_enumTypes[0].Descriptor()
70+}
71+
72+func (IssueState) Type() protoreflect.EnumType {
73+ return &file_ermite_issue_proto_enumTypes[0]
74+}
75+
76+func (x IssueState) Number() protoreflect.EnumNumber {
77+ return protoreflect.EnumNumber(x)
78+}
79+
80+// Deprecated: Use IssueState.Descriptor instead.
81+func (IssueState) EnumDescriptor() ([]byte, []int) {
82+ return file_ermite_issue_proto_rawDescGZIP(), []int{0}
83+}
84+
85+// Issue is a discussion in a repository.
86+type Issue struct {
87+ state protoimpl.MessageState `protogen:"open.v1"`
88+ // Repo is the name of the repository.
89+ Repo string `protobuf:"bytes,1,opt,name=repo,proto3" json:"repo,omitempty"`
90+ // Number identifies the issue within its repository.
91+ Number int64 `protobuf:"varint,2,opt,name=number,proto3" json:"number,omitempty"`
92+ // Title is the one-line title.
93+ Title string `protobuf:"bytes,3,opt,name=title,proto3" json:"title,omitempty"`
94+ // Body is the content of the issue. It may be empty.
95+ Body string `protobuf:"bytes,4,opt,name=body,proto3" json:"body,omitempty"`
96+ // State is where the issue is in its life.
97+ State IssueState `protobuf:"varint,5,opt,name=state,proto3,enum=ermite.IssueState" json:"state,omitempty"`
98+ // Author names who created the issue: the label of the token or session
99+ // that was used ("root" for the root token, "owner" for a web session), or,
100+ // for an issue approved from the public form, the unverified display name
101+ // the visitor typed (or "anonymous"). It is informational and never proves
102+ // identity: labels are not unique, the token may be gone, and a visitor can
103+ // type "owner".
104+ Author string `protobuf:"bytes,6,opt,name=author,proto3" json:"author,omitempty"`
105+ // CreatedAt is when the issue was created.
106+ CreatedAt *timestamppb.Timestamp `protobuf:"bytes,7,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"`
107+ // UpdatedAt is when the issue last changed.
108+ UpdatedAt *timestamppb.Timestamp `protobuf:"bytes,8,opt,name=updated_at,json=updatedAt,proto3" json:"updated_at,omitempty"`
109+ // CommentCount is the number of comments on the issue.
110+ CommentCount int32 `protobuf:"varint,9,opt,name=comment_count,json=commentCount,proto3" json:"comment_count,omitempty"`
111+ unknownFields protoimpl.UnknownFields
112+ sizeCache protoimpl.SizeCache
113+}
114+
115+func (x *Issue) Reset() {
116+ *x = Issue{}
117+ mi := &file_ermite_issue_proto_msgTypes[0]
118+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
119+ ms.StoreMessageInfo(mi)
120+}
121+
122+func (x *Issue) String() string {
123+ return protoimpl.X.MessageStringOf(x)
124+}
125+
126+func (*Issue) ProtoMessage() {}
127+
128+func (x *Issue) ProtoReflect() protoreflect.Message {
129+ mi := &file_ermite_issue_proto_msgTypes[0]
130+ if x != nil {
131+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
132+ if ms.LoadMessageInfo() == nil {
133+ ms.StoreMessageInfo(mi)
134+ }
135+ return ms
136+ }
137+ return mi.MessageOf(x)
138+}
139+
140+// Deprecated: Use Issue.ProtoReflect.Descriptor instead.
141+func (*Issue) Descriptor() ([]byte, []int) {
142+ return file_ermite_issue_proto_rawDescGZIP(), []int{0}
143+}
144+
145+func (x *Issue) GetRepo() string {
146+ if x != nil {
147+ return x.Repo
148+ }
149+ return ""
150+}
151+
152+func (x *Issue) GetNumber() int64 {
153+ if x != nil {
154+ return x.Number
155+ }
156+ return 0
157+}
158+
159+func (x *Issue) GetTitle() string {
160+ if x != nil {
161+ return x.Title
162+ }
163+ return ""
164+}
165+
166+func (x *Issue) GetBody() string {
167+ if x != nil {
168+ return x.Body
169+ }
170+ return ""
171+}
172+
173+func (x *Issue) GetState() IssueState {
174+ if x != nil {
175+ return x.State
176+ }
177+ return IssueState_ISSUE_STATE_UNSPECIFIED
178+}
179+
180+func (x *Issue) GetAuthor() string {
181+ if x != nil {
182+ return x.Author
183+ }
184+ return ""
185+}
186+
187+func (x *Issue) GetCreatedAt() *timestamppb.Timestamp {
188+ if x != nil {
189+ return x.CreatedAt
190+ }
191+ return nil
192+}
193+
194+func (x *Issue) GetUpdatedAt() *timestamppb.Timestamp {
195+ if x != nil {
196+ return x.UpdatedAt
197+ }
198+ return nil
199+}
200+
201+func (x *Issue) GetCommentCount() int32 {
202+ if x != nil {
203+ return x.CommentCount
204+ }
205+ return 0
206+}
207+
208+// IssueComment is a note on an issue.
209+type IssueComment struct {
210+ state protoimpl.MessageState `protogen:"open.v1"`
211+ // Id identifies the comment within its issue. Opaque.
212+ Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
213+ // Author names who wrote the comment, like Issue.author.
214+ Author string `protobuf:"bytes,2,opt,name=author,proto3" json:"author,omitempty"`
215+ // Text is the content of the comment.
216+ Text string `protobuf:"bytes,3,opt,name=text,proto3" json:"text,omitempty"`
217+ // CreatedAt is when the comment was written.
218+ CreatedAt *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"`
219+ unknownFields protoimpl.UnknownFields
220+ sizeCache protoimpl.SizeCache
221+}
222+
223+func (x *IssueComment) Reset() {
224+ *x = IssueComment{}
225+ mi := &file_ermite_issue_proto_msgTypes[1]
226+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
227+ ms.StoreMessageInfo(mi)
228+}
229+
230+func (x *IssueComment) String() string {
231+ return protoimpl.X.MessageStringOf(x)
232+}
233+
234+func (*IssueComment) ProtoMessage() {}
235+
236+func (x *IssueComment) ProtoReflect() protoreflect.Message {
237+ mi := &file_ermite_issue_proto_msgTypes[1]
238+ if x != nil {
239+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
240+ if ms.LoadMessageInfo() == nil {
241+ ms.StoreMessageInfo(mi)
242+ }
243+ return ms
244+ }
245+ return mi.MessageOf(x)
246+}
247+
248+// Deprecated: Use IssueComment.ProtoReflect.Descriptor instead.
249+func (*IssueComment) Descriptor() ([]byte, []int) {
250+ return file_ermite_issue_proto_rawDescGZIP(), []int{1}
251+}
252+
253+func (x *IssueComment) GetId() string {
254+ if x != nil {
255+ return x.Id
256+ }
257+ return ""
258+}
259+
260+func (x *IssueComment) GetAuthor() string {
261+ if x != nil {
262+ return x.Author
263+ }
264+ return ""
265+}
266+
267+func (x *IssueComment) GetText() string {
268+ if x != nil {
269+ return x.Text
270+ }
271+ return ""
272+}
273+
274+func (x *IssueComment) GetCreatedAt() *timestamppb.Timestamp {
275+ if x != nil {
276+ return x.CreatedAt
277+ }
278+ return nil
279+}
280+
281+// CreateIssueRequest describes the issue to open.
282+type CreateIssueRequest struct {
283+ state protoimpl.MessageState `protogen:"open.v1"`
284+ // Repo is the repository name.
285+ Repo string `protobuf:"bytes,1,opt,name=repo,proto3" json:"repo,omitempty"`
286+ // Title is the one-line title. Required.
287+ Title string `protobuf:"bytes,2,opt,name=title,proto3" json:"title,omitempty"`
288+ // Body is the optional content.
289+ Body string `protobuf:"bytes,3,opt,name=body,proto3" json:"body,omitempty"`
290+ unknownFields protoimpl.UnknownFields
291+ sizeCache protoimpl.SizeCache
292+}
293+
294+func (x *CreateIssueRequest) Reset() {
295+ *x = CreateIssueRequest{}
296+ mi := &file_ermite_issue_proto_msgTypes[2]
297+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
298+ ms.StoreMessageInfo(mi)
299+}
300+
301+func (x *CreateIssueRequest) String() string {
302+ return protoimpl.X.MessageStringOf(x)
303+}
304+
305+func (*CreateIssueRequest) ProtoMessage() {}
306+
307+func (x *CreateIssueRequest) ProtoReflect() protoreflect.Message {
308+ mi := &file_ermite_issue_proto_msgTypes[2]
309+ if x != nil {
310+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
311+ if ms.LoadMessageInfo() == nil {
312+ ms.StoreMessageInfo(mi)
313+ }
314+ return ms
315+ }
316+ return mi.MessageOf(x)
317+}
318+
319+// Deprecated: Use CreateIssueRequest.ProtoReflect.Descriptor instead.
320+func (*CreateIssueRequest) Descriptor() ([]byte, []int) {
321+ return file_ermite_issue_proto_rawDescGZIP(), []int{2}
322+}
323+
324+func (x *CreateIssueRequest) GetRepo() string {
325+ if x != nil {
326+ return x.Repo
327+ }
328+ return ""
329+}
330+
331+func (x *CreateIssueRequest) GetTitle() string {
332+ if x != nil {
333+ return x.Title
334+ }
335+ return ""
336+}
337+
338+func (x *CreateIssueRequest) GetBody() string {
339+ if x != nil {
340+ return x.Body
341+ }
342+ return ""
343+}
344+
345+// CreateIssueResponse holds the new issue.
346+type CreateIssueResponse struct {
347+ state protoimpl.MessageState `protogen:"open.v1"`
348+ // Issue is the created issue.
349+ Issue *Issue `protobuf:"bytes,1,opt,name=issue,proto3" json:"issue,omitempty"`
350+ unknownFields protoimpl.UnknownFields
351+ sizeCache protoimpl.SizeCache
352+}
353+
354+func (x *CreateIssueResponse) Reset() {
355+ *x = CreateIssueResponse{}
356+ mi := &file_ermite_issue_proto_msgTypes[3]
357+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
358+ ms.StoreMessageInfo(mi)
359+}
360+
361+func (x *CreateIssueResponse) String() string {
362+ return protoimpl.X.MessageStringOf(x)
363+}
364+
365+func (*CreateIssueResponse) ProtoMessage() {}
366+
367+func (x *CreateIssueResponse) ProtoReflect() protoreflect.Message {
368+ mi := &file_ermite_issue_proto_msgTypes[3]
369+ if x != nil {
370+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
371+ if ms.LoadMessageInfo() == nil {
372+ ms.StoreMessageInfo(mi)
373+ }
374+ return ms
375+ }
376+ return mi.MessageOf(x)
377+}
378+
379+// Deprecated: Use CreateIssueResponse.ProtoReflect.Descriptor instead.
380+func (*CreateIssueResponse) Descriptor() ([]byte, []int) {
381+ return file_ermite_issue_proto_rawDescGZIP(), []int{3}
382+}
383+
384+func (x *CreateIssueResponse) GetIssue() *Issue {
385+ if x != nil {
386+ return x.Issue
387+ }
388+ return nil
389+}
390+
391+// ListIssuesRequest selects a page of issues.
392+type ListIssuesRequest struct {
393+ state protoimpl.MessageState `protogen:"open.v1"`
394+ // Repo is the repository name.
395+ Repo string `protobuf:"bytes,1,opt,name=repo,proto3" json:"repo,omitempty"`
396+ // State restricts the result to one state. ISSUE_STATE_UNSPECIFIED means
397+ // every state.
398+ State IssueState `protobuf:"varint,2,opt,name=state,proto3,enum=ermite.IssueState" json:"state,omitempty"`
399+ // PageSize is the maximum number of issues to return. 0 means the server
400+ // default; values above the server maximum are lowered to it.
401+ PageSize int32 `protobuf:"varint,3,opt,name=page_size,json=pageSize,proto3" json:"page_size,omitempty"`
402+ // PageToken is the next_page_token of a previous response, to continue the
403+ // same listing. Empty for the first page. Other fields must be unchanged.
404+ PageToken string `protobuf:"bytes,4,opt,name=page_token,json=pageToken,proto3" json:"page_token,omitempty"`
405+ unknownFields protoimpl.UnknownFields
406+ sizeCache protoimpl.SizeCache
407+}
408+
409+func (x *ListIssuesRequest) Reset() {
410+ *x = ListIssuesRequest{}
411+ mi := &file_ermite_issue_proto_msgTypes[4]
412+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
413+ ms.StoreMessageInfo(mi)
414+}
415+
416+func (x *ListIssuesRequest) String() string {
417+ return protoimpl.X.MessageStringOf(x)
418+}
419+
420+func (*ListIssuesRequest) ProtoMessage() {}
421+
422+func (x *ListIssuesRequest) ProtoReflect() protoreflect.Message {
423+ mi := &file_ermite_issue_proto_msgTypes[4]
424+ if x != nil {
425+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
426+ if ms.LoadMessageInfo() == nil {
427+ ms.StoreMessageInfo(mi)
428+ }
429+ return ms
430+ }
431+ return mi.MessageOf(x)
432+}
433+
434+// Deprecated: Use ListIssuesRequest.ProtoReflect.Descriptor instead.
435+func (*ListIssuesRequest) Descriptor() ([]byte, []int) {
436+ return file_ermite_issue_proto_rawDescGZIP(), []int{4}
437+}
438+
439+func (x *ListIssuesRequest) GetRepo() string {
440+ if x != nil {
441+ return x.Repo
442+ }
443+ return ""
444+}
445+
446+func (x *ListIssuesRequest) GetState() IssueState {
447+ if x != nil {
448+ return x.State
449+ }
450+ return IssueState_ISSUE_STATE_UNSPECIFIED
451+}
452+
453+func (x *ListIssuesRequest) GetPageSize() int32 {
454+ if x != nil {
455+ return x.PageSize
456+ }
457+ return 0
458+}
459+
460+func (x *ListIssuesRequest) GetPageToken() string {
461+ if x != nil {
462+ return x.PageToken
463+ }
464+ return ""
465+}
466+
467+// ListIssuesResponse is one page of issues.
468+type ListIssuesResponse struct {
469+ state protoimpl.MessageState `protogen:"open.v1"`
470+ // Issues are the issues of this page, highest number first.
471+ Issues []*Issue `protobuf:"bytes,1,rep,name=issues,proto3" json:"issues,omitempty"`
472+ // NextPageToken is passed as page_token to get the next page. Empty when
473+ // there is no next page.
474+ NextPageToken string `protobuf:"bytes,2,opt,name=next_page_token,json=nextPageToken,proto3" json:"next_page_token,omitempty"`
475+ unknownFields protoimpl.UnknownFields
476+ sizeCache protoimpl.SizeCache
477+}
478+
479+func (x *ListIssuesResponse) Reset() {
480+ *x = ListIssuesResponse{}
481+ mi := &file_ermite_issue_proto_msgTypes[5]
482+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
483+ ms.StoreMessageInfo(mi)
484+}
485+
486+func (x *ListIssuesResponse) String() string {
487+ return protoimpl.X.MessageStringOf(x)
488+}
489+
490+func (*ListIssuesResponse) ProtoMessage() {}
491+
492+func (x *ListIssuesResponse) ProtoReflect() protoreflect.Message {
493+ mi := &file_ermite_issue_proto_msgTypes[5]
494+ if x != nil {
495+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
496+ if ms.LoadMessageInfo() == nil {
497+ ms.StoreMessageInfo(mi)
498+ }
499+ return ms
500+ }
501+ return mi.MessageOf(x)
502+}
503+
504+// Deprecated: Use ListIssuesResponse.ProtoReflect.Descriptor instead.
505+func (*ListIssuesResponse) Descriptor() ([]byte, []int) {
506+ return file_ermite_issue_proto_rawDescGZIP(), []int{5}
507+}
508+
509+func (x *ListIssuesResponse) GetIssues() []*Issue {
510+ if x != nil {
511+ return x.Issues
512+ }
513+ return nil
514+}
515+
516+func (x *ListIssuesResponse) GetNextPageToken() string {
517+ if x != nil {
518+ return x.NextPageToken
519+ }
520+ return ""
521+}
522+
523+// GetIssueRequest selects an issue.
524+type GetIssueRequest struct {
525+ state protoimpl.MessageState `protogen:"open.v1"`
526+ // Repo is the repository name.
527+ Repo string `protobuf:"bytes,1,opt,name=repo,proto3" json:"repo,omitempty"`
528+ // Number is the issue number.
529+ Number int64 `protobuf:"varint,2,opt,name=number,proto3" json:"number,omitempty"`
530+ unknownFields protoimpl.UnknownFields
531+ sizeCache protoimpl.SizeCache
532+}
533+
534+func (x *GetIssueRequest) Reset() {
535+ *x = GetIssueRequest{}
536+ mi := &file_ermite_issue_proto_msgTypes[6]
537+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
538+ ms.StoreMessageInfo(mi)
539+}
540+
541+func (x *GetIssueRequest) String() string {
542+ return protoimpl.X.MessageStringOf(x)
543+}
544+
545+func (*GetIssueRequest) ProtoMessage() {}
546+
547+func (x *GetIssueRequest) ProtoReflect() protoreflect.Message {
548+ mi := &file_ermite_issue_proto_msgTypes[6]
549+ if x != nil {
550+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
551+ if ms.LoadMessageInfo() == nil {
552+ ms.StoreMessageInfo(mi)
553+ }
554+ return ms
555+ }
556+ return mi.MessageOf(x)
557+}
558+
559+// Deprecated: Use GetIssueRequest.ProtoReflect.Descriptor instead.
560+func (*GetIssueRequest) Descriptor() ([]byte, []int) {
561+ return file_ermite_issue_proto_rawDescGZIP(), []int{6}
562+}
563+
564+func (x *GetIssueRequest) GetRepo() string {
565+ if x != nil {
566+ return x.Repo
567+ }
568+ return ""
569+}
570+
571+func (x *GetIssueRequest) GetNumber() int64 {
572+ if x != nil {
573+ return x.Number
574+ }
575+ return 0
576+}
577+
578+// GetIssueResponse holds an issue with its comments.
579+type GetIssueResponse struct {
580+ state protoimpl.MessageState `protogen:"open.v1"`
581+ // Issue is the issue.
582+ Issue *Issue `protobuf:"bytes,1,opt,name=issue,proto3" json:"issue,omitempty"`
583+ // Comments are every comment of the issue, oldest first.
584+ Comments []*IssueComment `protobuf:"bytes,2,rep,name=comments,proto3" json:"comments,omitempty"`
585+ unknownFields protoimpl.UnknownFields
586+ sizeCache protoimpl.SizeCache
587+}
588+
589+func (x *GetIssueResponse) Reset() {
590+ *x = GetIssueResponse{}
591+ mi := &file_ermite_issue_proto_msgTypes[7]
592+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
593+ ms.StoreMessageInfo(mi)
594+}
595+
596+func (x *GetIssueResponse) String() string {
597+ return protoimpl.X.MessageStringOf(x)
598+}
599+
600+func (*GetIssueResponse) ProtoMessage() {}
601+
602+func (x *GetIssueResponse) ProtoReflect() protoreflect.Message {
603+ mi := &file_ermite_issue_proto_msgTypes[7]
604+ if x != nil {
605+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
606+ if ms.LoadMessageInfo() == nil {
607+ ms.StoreMessageInfo(mi)
608+ }
609+ return ms
610+ }
611+ return mi.MessageOf(x)
612+}
613+
614+// Deprecated: Use GetIssueResponse.ProtoReflect.Descriptor instead.
615+func (*GetIssueResponse) Descriptor() ([]byte, []int) {
616+ return file_ermite_issue_proto_rawDescGZIP(), []int{7}
617+}
618+
619+func (x *GetIssueResponse) GetIssue() *Issue {
620+ if x != nil {
621+ return x.Issue
622+ }
623+ return nil
624+}
625+
626+func (x *GetIssueResponse) GetComments() []*IssueComment {
627+ if x != nil {
628+ return x.Comments
629+ }
630+ return nil
631+}
632+
633+// AddIssueCommentRequest describes the comment to add.
634+type AddIssueCommentRequest struct {
635+ state protoimpl.MessageState `protogen:"open.v1"`
636+ // Repo is the repository name.
637+ Repo string `protobuf:"bytes,1,opt,name=repo,proto3" json:"repo,omitempty"`
638+ // Number is the issue number.
639+ Number int64 `protobuf:"varint,2,opt,name=number,proto3" json:"number,omitempty"`
640+ // Text is the content. Required.
641+ Text string `protobuf:"bytes,3,opt,name=text,proto3" json:"text,omitempty"`
642+ unknownFields protoimpl.UnknownFields
643+ sizeCache protoimpl.SizeCache
644+}
645+
646+func (x *AddIssueCommentRequest) Reset() {
647+ *x = AddIssueCommentRequest{}
648+ mi := &file_ermite_issue_proto_msgTypes[8]
649+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
650+ ms.StoreMessageInfo(mi)
651+}
652+
653+func (x *AddIssueCommentRequest) String() string {
654+ return protoimpl.X.MessageStringOf(x)
655+}
656+
657+func (*AddIssueCommentRequest) ProtoMessage() {}
658+
659+func (x *AddIssueCommentRequest) ProtoReflect() protoreflect.Message {
660+ mi := &file_ermite_issue_proto_msgTypes[8]
661+ if x != nil {
662+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
663+ if ms.LoadMessageInfo() == nil {
664+ ms.StoreMessageInfo(mi)
665+ }
666+ return ms
667+ }
668+ return mi.MessageOf(x)
669+}
670+
671+// Deprecated: Use AddIssueCommentRequest.ProtoReflect.Descriptor instead.
672+func (*AddIssueCommentRequest) Descriptor() ([]byte, []int) {
673+ return file_ermite_issue_proto_rawDescGZIP(), []int{8}
674+}
675+
676+func (x *AddIssueCommentRequest) GetRepo() string {
677+ if x != nil {
678+ return x.Repo
679+ }
680+ return ""
681+}
682+
683+func (x *AddIssueCommentRequest) GetNumber() int64 {
684+ if x != nil {
685+ return x.Number
686+ }
687+ return 0
688+}
689+
690+func (x *AddIssueCommentRequest) GetText() string {
691+ if x != nil {
692+ return x.Text
693+ }
694+ return ""
695+}
696+
697+// AddIssueCommentResponse holds the new comment.
698+type AddIssueCommentResponse struct {
699+ state protoimpl.MessageState `protogen:"open.v1"`
700+ // Comment is the created comment.
701+ Comment *IssueComment `protobuf:"bytes,1,opt,name=comment,proto3" json:"comment,omitempty"`
702+ unknownFields protoimpl.UnknownFields
703+ sizeCache protoimpl.SizeCache
704+}
705+
706+func (x *AddIssueCommentResponse) Reset() {
707+ *x = AddIssueCommentResponse{}
708+ mi := &file_ermite_issue_proto_msgTypes[9]
709+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
710+ ms.StoreMessageInfo(mi)
711+}
712+
713+func (x *AddIssueCommentResponse) String() string {
714+ return protoimpl.X.MessageStringOf(x)
715+}
716+
717+func (*AddIssueCommentResponse) ProtoMessage() {}
718+
719+func (x *AddIssueCommentResponse) ProtoReflect() protoreflect.Message {
720+ mi := &file_ermite_issue_proto_msgTypes[9]
721+ if x != nil {
722+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
723+ if ms.LoadMessageInfo() == nil {
724+ ms.StoreMessageInfo(mi)
725+ }
726+ return ms
727+ }
728+ return mi.MessageOf(x)
729+}
730+
731+// Deprecated: Use AddIssueCommentResponse.ProtoReflect.Descriptor instead.
732+func (*AddIssueCommentResponse) Descriptor() ([]byte, []int) {
733+ return file_ermite_issue_proto_rawDescGZIP(), []int{9}
734+}
735+
736+func (x *AddIssueCommentResponse) GetComment() *IssueComment {
737+ if x != nil {
738+ return x.Comment
739+ }
740+ return nil
741+}
742+
743+// CloseIssueRequest names the issue to close.
744+type CloseIssueRequest struct {
745+ state protoimpl.MessageState `protogen:"open.v1"`
746+ // Repo is the repository name.
747+ Repo string `protobuf:"bytes,1,opt,name=repo,proto3" json:"repo,omitempty"`
748+ // Number is the issue number.
749+ Number int64 `protobuf:"varint,2,opt,name=number,proto3" json:"number,omitempty"`
750+ unknownFields protoimpl.UnknownFields
751+ sizeCache protoimpl.SizeCache
752+}
753+
754+func (x *CloseIssueRequest) Reset() {
755+ *x = CloseIssueRequest{}
756+ mi := &file_ermite_issue_proto_msgTypes[10]
757+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
758+ ms.StoreMessageInfo(mi)
759+}
760+
761+func (x *CloseIssueRequest) String() string {
762+ return protoimpl.X.MessageStringOf(x)
763+}
764+
765+func (*CloseIssueRequest) ProtoMessage() {}
766+
767+func (x *CloseIssueRequest) ProtoReflect() protoreflect.Message {
768+ mi := &file_ermite_issue_proto_msgTypes[10]
769+ if x != nil {
770+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
771+ if ms.LoadMessageInfo() == nil {
772+ ms.StoreMessageInfo(mi)
773+ }
774+ return ms
775+ }
776+ return mi.MessageOf(x)
777+}
778+
779+// Deprecated: Use CloseIssueRequest.ProtoReflect.Descriptor instead.
780+func (*CloseIssueRequest) Descriptor() ([]byte, []int) {
781+ return file_ermite_issue_proto_rawDescGZIP(), []int{10}
782+}
783+
784+func (x *CloseIssueRequest) GetRepo() string {
785+ if x != nil {
786+ return x.Repo
787+ }
788+ return ""
789+}
790+
791+func (x *CloseIssueRequest) GetNumber() int64 {
792+ if x != nil {
793+ return x.Number
794+ }
795+ return 0
796+}
797+
798+// CloseIssueResponse holds the issue after closing.
799+type CloseIssueResponse struct {
800+ state protoimpl.MessageState `protogen:"open.v1"`
801+ // Issue is the closed issue.
802+ Issue *Issue `protobuf:"bytes,1,opt,name=issue,proto3" json:"issue,omitempty"`
803+ unknownFields protoimpl.UnknownFields
804+ sizeCache protoimpl.SizeCache
805+}
806+
807+func (x *CloseIssueResponse) Reset() {
808+ *x = CloseIssueResponse{}
809+ mi := &file_ermite_issue_proto_msgTypes[11]
810+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
811+ ms.StoreMessageInfo(mi)
812+}
813+
814+func (x *CloseIssueResponse) String() string {
815+ return protoimpl.X.MessageStringOf(x)
816+}
817+
818+func (*CloseIssueResponse) ProtoMessage() {}
819+
820+func (x *CloseIssueResponse) ProtoReflect() protoreflect.Message {
821+ mi := &file_ermite_issue_proto_msgTypes[11]
822+ if x != nil {
823+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
824+ if ms.LoadMessageInfo() == nil {
825+ ms.StoreMessageInfo(mi)
826+ }
827+ return ms
828+ }
829+ return mi.MessageOf(x)
830+}
831+
832+// Deprecated: Use CloseIssueResponse.ProtoReflect.Descriptor instead.
833+func (*CloseIssueResponse) Descriptor() ([]byte, []int) {
834+ return file_ermite_issue_proto_rawDescGZIP(), []int{11}
835+}
836+
837+func (x *CloseIssueResponse) GetIssue() *Issue {
838+ if x != nil {
839+ return x.Issue
840+ }
841+ return nil
842+}
843+
844+// ReopenIssueRequest names the issue to reopen.
845+type ReopenIssueRequest struct {
846+ state protoimpl.MessageState `protogen:"open.v1"`
847+ // Repo is the repository name.
848+ Repo string `protobuf:"bytes,1,opt,name=repo,proto3" json:"repo,omitempty"`
849+ // Number is the issue number.
850+ Number int64 `protobuf:"varint,2,opt,name=number,proto3" json:"number,omitempty"`
851+ unknownFields protoimpl.UnknownFields
852+ sizeCache protoimpl.SizeCache
853+}
854+
855+func (x *ReopenIssueRequest) Reset() {
856+ *x = ReopenIssueRequest{}
857+ mi := &file_ermite_issue_proto_msgTypes[12]
858+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
859+ ms.StoreMessageInfo(mi)
860+}
861+
862+func (x *ReopenIssueRequest) String() string {
863+ return protoimpl.X.MessageStringOf(x)
864+}
865+
866+func (*ReopenIssueRequest) ProtoMessage() {}
867+
868+func (x *ReopenIssueRequest) ProtoReflect() protoreflect.Message {
869+ mi := &file_ermite_issue_proto_msgTypes[12]
870+ if x != nil {
871+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
872+ if ms.LoadMessageInfo() == nil {
873+ ms.StoreMessageInfo(mi)
874+ }
875+ return ms
876+ }
877+ return mi.MessageOf(x)
878+}
879+
880+// Deprecated: Use ReopenIssueRequest.ProtoReflect.Descriptor instead.
881+func (*ReopenIssueRequest) Descriptor() ([]byte, []int) {
882+ return file_ermite_issue_proto_rawDescGZIP(), []int{12}
883+}
884+
885+func (x *ReopenIssueRequest) GetRepo() string {
886+ if x != nil {
887+ return x.Repo
888+ }
889+ return ""
890+}
891+
892+func (x *ReopenIssueRequest) GetNumber() int64 {
893+ if x != nil {
894+ return x.Number
895+ }
896+ return 0
897+}
898+
899+// ReopenIssueResponse holds the issue after reopening.
900+type ReopenIssueResponse struct {
901+ state protoimpl.MessageState `protogen:"open.v1"`
902+ // Issue is the reopened issue.
903+ Issue *Issue `protobuf:"bytes,1,opt,name=issue,proto3" json:"issue,omitempty"`
904+ unknownFields protoimpl.UnknownFields
905+ sizeCache protoimpl.SizeCache
906+}
907+
908+func (x *ReopenIssueResponse) Reset() {
909+ *x = ReopenIssueResponse{}
910+ mi := &file_ermite_issue_proto_msgTypes[13]
911+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
912+ ms.StoreMessageInfo(mi)
913+}
914+
915+func (x *ReopenIssueResponse) String() string {
916+ return protoimpl.X.MessageStringOf(x)
917+}
918+
919+func (*ReopenIssueResponse) ProtoMessage() {}
920+
921+func (x *ReopenIssueResponse) ProtoReflect() protoreflect.Message {
922+ mi := &file_ermite_issue_proto_msgTypes[13]
923+ if x != nil {
924+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
925+ if ms.LoadMessageInfo() == nil {
926+ ms.StoreMessageInfo(mi)
927+ }
928+ return ms
929+ }
930+ return mi.MessageOf(x)
931+}
932+
933+// Deprecated: Use ReopenIssueResponse.ProtoReflect.Descriptor instead.
934+func (*ReopenIssueResponse) Descriptor() ([]byte, []int) {
935+ return file_ermite_issue_proto_rawDescGZIP(), []int{13}
936+}
937+
938+func (x *ReopenIssueResponse) GetIssue() *Issue {
939+ if x != nil {
940+ return x.Issue
941+ }
942+ return nil
943+}
944+
945+// PendingIssue is an issue awaiting moderation. It has no number yet.
946+type PendingIssue struct {
947+ state protoimpl.MessageState `protogen:"open.v1"`
948+ // Id identifies the pending issue. Opaque internal id, not a number.
949+ Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
950+ // Repo is the name of the repository.
951+ Repo string `protobuf:"bytes,2,opt,name=repo,proto3" json:"repo,omitempty"`
952+ // Title is the one-line title.
953+ Title string `protobuf:"bytes,3,opt,name=title,proto3" json:"title,omitempty"`
954+ // Body is the content. It may be empty.
955+ Body string `protobuf:"bytes,4,opt,name=body,proto3" json:"body,omitempty"`
956+ // Author is the optional display name given by the anonymous submitter. It
957+ // may be empty or "anonymous", and is not verified: it never proves
958+ // identity (a visitor can type "owner"). It becomes Issue.author on approval.
959+ Author string `protobuf:"bytes,5,opt,name=author,proto3" json:"author,omitempty"`
960+ // CreatedAt is when the issue was submitted.
961+ CreatedAt *timestamppb.Timestamp `protobuf:"bytes,6,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"`
962+ unknownFields protoimpl.UnknownFields
963+ sizeCache protoimpl.SizeCache
964+}
965+
966+func (x *PendingIssue) Reset() {
967+ *x = PendingIssue{}
968+ mi := &file_ermite_issue_proto_msgTypes[14]
969+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
970+ ms.StoreMessageInfo(mi)
971+}
972+
973+func (x *PendingIssue) String() string {
974+ return protoimpl.X.MessageStringOf(x)
975+}
976+
977+func (*PendingIssue) ProtoMessage() {}
978+
979+func (x *PendingIssue) ProtoReflect() protoreflect.Message {
980+ mi := &file_ermite_issue_proto_msgTypes[14]
981+ if x != nil {
982+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
983+ if ms.LoadMessageInfo() == nil {
984+ ms.StoreMessageInfo(mi)
985+ }
986+ return ms
987+ }
988+ return mi.MessageOf(x)
989+}
990+
991+// Deprecated: Use PendingIssue.ProtoReflect.Descriptor instead.
992+func (*PendingIssue) Descriptor() ([]byte, []int) {
993+ return file_ermite_issue_proto_rawDescGZIP(), []int{14}
994+}
995+
996+func (x *PendingIssue) GetId() string {
997+ if x != nil {
998+ return x.Id
999+ }
1000+ return ""
1001+}
1002+
1003+func (x *PendingIssue) GetRepo() string {
1004+ if x != nil {
1005+ return x.Repo
1006+ }
1007+ return ""
1008+}
1009+
1010+func (x *PendingIssue) GetTitle() string {
1011+ if x != nil {
1012+ return x.Title
1013+ }
1014+ return ""
1015+}
1016+
1017+func (x *PendingIssue) GetBody() string {
1018+ if x != nil {
1019+ return x.Body
1020+ }
1021+ return ""
1022+}
1023+
1024+func (x *PendingIssue) GetAuthor() string {
1025+ if x != nil {
1026+ return x.Author
1027+ }
1028+ return ""
1029+}
1030+
1031+func (x *PendingIssue) GetCreatedAt() *timestamppb.Timestamp {
1032+ if x != nil {
1033+ return x.CreatedAt
1034+ }
1035+ return nil
1036+}
1037+
1038+// ListPendingIssuesRequest selects a page of pending issues.
1039+type ListPendingIssuesRequest struct {
1040+ state protoimpl.MessageState `protogen:"open.v1"`
1041+ // Repo is the repository name. Empty means every repository.
1042+ Repo string `protobuf:"bytes,1,opt,name=repo,proto3" json:"repo,omitempty"`
1043+ // PageSize is the maximum number of issues to return. 0 means the server
1044+ // default; values above the server maximum are lowered to it.
1045+ PageSize int32 `protobuf:"varint,2,opt,name=page_size,json=pageSize,proto3" json:"page_size,omitempty"`
1046+ // PageToken is the next_page_token of a previous response, to continue the
1047+ // same listing. Empty for the first page. Other fields must be unchanged.
1048+ PageToken string `protobuf:"bytes,3,opt,name=page_token,json=pageToken,proto3" json:"page_token,omitempty"`
1049+ unknownFields protoimpl.UnknownFields
1050+ sizeCache protoimpl.SizeCache
1051+}
1052+
1053+func (x *ListPendingIssuesRequest) Reset() {
1054+ *x = ListPendingIssuesRequest{}
1055+ mi := &file_ermite_issue_proto_msgTypes[15]
1056+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
1057+ ms.StoreMessageInfo(mi)
1058+}
1059+
1060+func (x *ListPendingIssuesRequest) String() string {
1061+ return protoimpl.X.MessageStringOf(x)
1062+}
1063+
1064+func (*ListPendingIssuesRequest) ProtoMessage() {}
1065+
1066+func (x *ListPendingIssuesRequest) ProtoReflect() protoreflect.Message {
1067+ mi := &file_ermite_issue_proto_msgTypes[15]
1068+ if x != nil {
1069+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
1070+ if ms.LoadMessageInfo() == nil {
1071+ ms.StoreMessageInfo(mi)
1072+ }
1073+ return ms
1074+ }
1075+ return mi.MessageOf(x)
1076+}
1077+
1078+// Deprecated: Use ListPendingIssuesRequest.ProtoReflect.Descriptor instead.
1079+func (*ListPendingIssuesRequest) Descriptor() ([]byte, []int) {
1080+ return file_ermite_issue_proto_rawDescGZIP(), []int{15}
1081+}
1082+
1083+func (x *ListPendingIssuesRequest) GetRepo() string {
1084+ if x != nil {
1085+ return x.Repo
1086+ }
1087+ return ""
1088+}
1089+
1090+func (x *ListPendingIssuesRequest) GetPageSize() int32 {
1091+ if x != nil {
1092+ return x.PageSize
1093+ }
1094+ return 0
1095+}
1096+
1097+func (x *ListPendingIssuesRequest) GetPageToken() string {
1098+ if x != nil {
1099+ return x.PageToken
1100+ }
1101+ return ""
1102+}
1103+
1104+// ListPendingIssuesResponse is one page of pending issues.
1105+type ListPendingIssuesResponse struct {
1106+ state protoimpl.MessageState `protogen:"open.v1"`
1107+ // Issues are the pending issues of this page, oldest first.
1108+ Issues []*PendingIssue `protobuf:"bytes,1,rep,name=issues,proto3" json:"issues,omitempty"`
1109+ // NextPageToken is passed as page_token to get the next page. Empty when
1110+ // there is no next page.
1111+ NextPageToken string `protobuf:"bytes,2,opt,name=next_page_token,json=nextPageToken,proto3" json:"next_page_token,omitempty"`
1112+ unknownFields protoimpl.UnknownFields
1113+ sizeCache protoimpl.SizeCache
1114+}
1115+
1116+func (x *ListPendingIssuesResponse) Reset() {
1117+ *x = ListPendingIssuesResponse{}
1118+ mi := &file_ermite_issue_proto_msgTypes[16]
1119+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
1120+ ms.StoreMessageInfo(mi)
1121+}
1122+
1123+func (x *ListPendingIssuesResponse) String() string {
1124+ return protoimpl.X.MessageStringOf(x)
1125+}
1126+
1127+func (*ListPendingIssuesResponse) ProtoMessage() {}
1128+
1129+func (x *ListPendingIssuesResponse) ProtoReflect() protoreflect.Message {
1130+ mi := &file_ermite_issue_proto_msgTypes[16]
1131+ if x != nil {
1132+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
1133+ if ms.LoadMessageInfo() == nil {
1134+ ms.StoreMessageInfo(mi)
1135+ }
1136+ return ms
1137+ }
1138+ return mi.MessageOf(x)
1139+}
1140+
1141+// Deprecated: Use ListPendingIssuesResponse.ProtoReflect.Descriptor instead.
1142+func (*ListPendingIssuesResponse) Descriptor() ([]byte, []int) {
1143+ return file_ermite_issue_proto_rawDescGZIP(), []int{16}
1144+}
1145+
1146+func (x *ListPendingIssuesResponse) GetIssues() []*PendingIssue {
1147+ if x != nil {
1148+ return x.Issues
1149+ }
1150+ return nil
1151+}
1152+
1153+func (x *ListPendingIssuesResponse) GetNextPageToken() string {
1154+ if x != nil {
1155+ return x.NextPageToken
1156+ }
1157+ return ""
1158+}
1159+
1160+// ApproveIssueRequest names the pending issue to approve.
1161+type ApproveIssueRequest struct {
1162+ state protoimpl.MessageState `protogen:"open.v1"`
1163+ // Repo is the repository name.
1164+ Repo string `protobuf:"bytes,1,opt,name=repo,proto3" json:"repo,omitempty"`
1165+ // Id is the PendingIssue.id.
1166+ Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"`
1167+ unknownFields protoimpl.UnknownFields
1168+ sizeCache protoimpl.SizeCache
1169+}
1170+
1171+func (x *ApproveIssueRequest) Reset() {
1172+ *x = ApproveIssueRequest{}
1173+ mi := &file_ermite_issue_proto_msgTypes[17]
1174+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
1175+ ms.StoreMessageInfo(mi)
1176+}
1177+
1178+func (x *ApproveIssueRequest) String() string {
1179+ return protoimpl.X.MessageStringOf(x)
1180+}
1181+
1182+func (*ApproveIssueRequest) ProtoMessage() {}
1183+
1184+func (x *ApproveIssueRequest) ProtoReflect() protoreflect.Message {
1185+ mi := &file_ermite_issue_proto_msgTypes[17]
1186+ if x != nil {
1187+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
1188+ if ms.LoadMessageInfo() == nil {
1189+ ms.StoreMessageInfo(mi)
1190+ }
1191+ return ms
1192+ }
1193+ return mi.MessageOf(x)
1194+}
1195+
1196+// Deprecated: Use ApproveIssueRequest.ProtoReflect.Descriptor instead.
1197+func (*ApproveIssueRequest) Descriptor() ([]byte, []int) {
1198+ return file_ermite_issue_proto_rawDescGZIP(), []int{17}
1199+}
1200+
1201+func (x *ApproveIssueRequest) GetRepo() string {
1202+ if x != nil {
1203+ return x.Repo
1204+ }
1205+ return ""
1206+}
1207+
1208+func (x *ApproveIssueRequest) GetId() string {
1209+ if x != nil {
1210+ return x.Id
1211+ }
1212+ return ""
1213+}
1214+
1215+// ApproveIssueResponse holds the approved issue.
1216+type ApproveIssueResponse struct {
1217+ state protoimpl.MessageState `protogen:"open.v1"`
1218+ // Issue is the approved issue, with its new number and state
1219+ // ISSUE_STATE_OPEN.
1220+ Issue *Issue `protobuf:"bytes,1,opt,name=issue,proto3" json:"issue,omitempty"`
1221+ unknownFields protoimpl.UnknownFields
1222+ sizeCache protoimpl.SizeCache
1223+}
1224+
1225+func (x *ApproveIssueResponse) Reset() {
1226+ *x = ApproveIssueResponse{}
1227+ mi := &file_ermite_issue_proto_msgTypes[18]
1228+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
1229+ ms.StoreMessageInfo(mi)
1230+}
1231+
1232+func (x *ApproveIssueResponse) String() string {
1233+ return protoimpl.X.MessageStringOf(x)
1234+}
1235+
1236+func (*ApproveIssueResponse) ProtoMessage() {}
1237+
1238+func (x *ApproveIssueResponse) ProtoReflect() protoreflect.Message {
1239+ mi := &file_ermite_issue_proto_msgTypes[18]
1240+ if x != nil {
1241+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
1242+ if ms.LoadMessageInfo() == nil {
1243+ ms.StoreMessageInfo(mi)
1244+ }
1245+ return ms
1246+ }
1247+ return mi.MessageOf(x)
1248+}
1249+
1250+// Deprecated: Use ApproveIssueResponse.ProtoReflect.Descriptor instead.
1251+func (*ApproveIssueResponse) Descriptor() ([]byte, []int) {
1252+ return file_ermite_issue_proto_rawDescGZIP(), []int{18}
1253+}
1254+
1255+func (x *ApproveIssueResponse) GetIssue() *Issue {
1256+ if x != nil {
1257+ return x.Issue
1258+ }
1259+ return nil
1260+}
1261+
1262+// RejectIssueRequest names the pending issue to reject.
1263+type RejectIssueRequest struct {
1264+ state protoimpl.MessageState `protogen:"open.v1"`
1265+ // Repo is the repository name.
1266+ Repo string `protobuf:"bytes,1,opt,name=repo,proto3" json:"repo,omitempty"`
1267+ // Id is the PendingIssue.id.
1268+ Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"`
1269+ // Ban also bans the submitter for a while.
1270+ Ban bool `protobuf:"varint,3,opt,name=ban,proto3" json:"ban,omitempty"`
1271+ unknownFields protoimpl.UnknownFields
1272+ sizeCache protoimpl.SizeCache
1273+}
1274+
1275+func (x *RejectIssueRequest) Reset() {
1276+ *x = RejectIssueRequest{}
1277+ mi := &file_ermite_issue_proto_msgTypes[19]
1278+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
1279+ ms.StoreMessageInfo(mi)
1280+}
1281+
1282+func (x *RejectIssueRequest) String() string {
1283+ return protoimpl.X.MessageStringOf(x)
1284+}
1285+
1286+func (*RejectIssueRequest) ProtoMessage() {}
1287+
1288+func (x *RejectIssueRequest) ProtoReflect() protoreflect.Message {
1289+ mi := &file_ermite_issue_proto_msgTypes[19]
1290+ if x != nil {
1291+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
1292+ if ms.LoadMessageInfo() == nil {
1293+ ms.StoreMessageInfo(mi)
1294+ }
1295+ return ms
1296+ }
1297+ return mi.MessageOf(x)
1298+}
1299+
1300+// Deprecated: Use RejectIssueRequest.ProtoReflect.Descriptor instead.
1301+func (*RejectIssueRequest) Descriptor() ([]byte, []int) {
1302+ return file_ermite_issue_proto_rawDescGZIP(), []int{19}
1303+}
1304+
1305+func (x *RejectIssueRequest) GetRepo() string {
1306+ if x != nil {
1307+ return x.Repo
1308+ }
1309+ return ""
1310+}
1311+
1312+func (x *RejectIssueRequest) GetId() string {
1313+ if x != nil {
1314+ return x.Id
1315+ }
1316+ return ""
1317+}
1318+
1319+func (x *RejectIssueRequest) GetBan() bool {
1320+ if x != nil {
1321+ return x.Ban
1322+ }
1323+ return false
1324+}
1325+
1326+// RejectIssueResponse is empty: the pending issue is gone.
1327+type RejectIssueResponse struct {
1328+ state protoimpl.MessageState `protogen:"open.v1"`
1329+ unknownFields protoimpl.UnknownFields
1330+ sizeCache protoimpl.SizeCache
1331+}
1332+
1333+func (x *RejectIssueResponse) Reset() {
1334+ *x = RejectIssueResponse{}
1335+ mi := &file_ermite_issue_proto_msgTypes[20]
1336+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
1337+ ms.StoreMessageInfo(mi)
1338+}
1339+
1340+func (x *RejectIssueResponse) String() string {
1341+ return protoimpl.X.MessageStringOf(x)
1342+}
1343+
1344+func (*RejectIssueResponse) ProtoMessage() {}
1345+
1346+func (x *RejectIssueResponse) ProtoReflect() protoreflect.Message {
1347+ mi := &file_ermite_issue_proto_msgTypes[20]
1348+ if x != nil {
1349+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
1350+ if ms.LoadMessageInfo() == nil {
1351+ ms.StoreMessageInfo(mi)
1352+ }
1353+ return ms
1354+ }
1355+ return mi.MessageOf(x)
1356+}
1357+
1358+// Deprecated: Use RejectIssueResponse.ProtoReflect.Descriptor instead.
1359+func (*RejectIssueResponse) Descriptor() ([]byte, []int) {
1360+ return file_ermite_issue_proto_rawDescGZIP(), []int{20}
1361+}
1362+
1363+var File_ermite_issue_proto protoreflect.FileDescriptor
1364+
1365+const file_ermite_issue_proto_rawDesc = "" +
1366+ "\n" +
1367+ "\x12ermite/issue.proto\x12\x06ermite\x1a\x1fgoogle/protobuf/timestamp.proto\"\xba\x02\n" +
1368+ "\x05Issue\x12\x12\n" +
1369+ "\x04repo\x18\x01 \x01(\tR\x04repo\x12\x16\n" +
1370+ "\x06number\x18\x02 \x01(\x03R\x06number\x12\x14\n" +
1371+ "\x05title\x18\x03 \x01(\tR\x05title\x12\x12\n" +
1372+ "\x04body\x18\x04 \x01(\tR\x04body\x12(\n" +
1373+ "\x05state\x18\x05 \x01(\x0e2\x12.ermite.IssueStateR\x05state\x12\x16\n" +
1374+ "\x06author\x18\x06 \x01(\tR\x06author\x129\n" +
1375+ "\n" +
1376+ "created_at\x18\a \x01(\v2\x1a.google.protobuf.TimestampR\tcreatedAt\x129\n" +
1377+ "\n" +
1378+ "updated_at\x18\b \x01(\v2\x1a.google.protobuf.TimestampR\tupdatedAt\x12#\n" +
1379+ "\rcomment_count\x18\t \x01(\x05R\fcommentCount\"\x85\x01\n" +
1380+ "\fIssueComment\x12\x0e\n" +
1381+ "\x02id\x18\x01 \x01(\tR\x02id\x12\x16\n" +
1382+ "\x06author\x18\x02 \x01(\tR\x06author\x12\x12\n" +
1383+ "\x04text\x18\x03 \x01(\tR\x04text\x129\n" +
1384+ "\n" +
1385+ "created_at\x18\x04 \x01(\v2\x1a.google.protobuf.TimestampR\tcreatedAt\"R\n" +
1386+ "\x12CreateIssueRequest\x12\x12\n" +
1387+ "\x04repo\x18\x01 \x01(\tR\x04repo\x12\x14\n" +
1388+ "\x05title\x18\x02 \x01(\tR\x05title\x12\x12\n" +
1389+ "\x04body\x18\x03 \x01(\tR\x04body\":\n" +
1390+ "\x13CreateIssueResponse\x12#\n" +
1391+ "\x05issue\x18\x01 \x01(\v2\r.ermite.IssueR\x05issue\"\x8d\x01\n" +
1392+ "\x11ListIssuesRequest\x12\x12\n" +
1393+ "\x04repo\x18\x01 \x01(\tR\x04repo\x12(\n" +
1394+ "\x05state\x18\x02 \x01(\x0e2\x12.ermite.IssueStateR\x05state\x12\x1b\n" +
1395+ "\tpage_size\x18\x03 \x01(\x05R\bpageSize\x12\x1d\n" +
1396+ "\n" +
1397+ "page_token\x18\x04 \x01(\tR\tpageToken\"c\n" +
1398+ "\x12ListIssuesResponse\x12%\n" +
1399+ "\x06issues\x18\x01 \x03(\v2\r.ermite.IssueR\x06issues\x12&\n" +
1400+ "\x0fnext_page_token\x18\x02 \x01(\tR\rnextPageToken\"=\n" +
1401+ "\x0fGetIssueRequest\x12\x12\n" +
1402+ "\x04repo\x18\x01 \x01(\tR\x04repo\x12\x16\n" +
1403+ "\x06number\x18\x02 \x01(\x03R\x06number\"i\n" +
1404+ "\x10GetIssueResponse\x12#\n" +
1405+ "\x05issue\x18\x01 \x01(\v2\r.ermite.IssueR\x05issue\x120\n" +
1406+ "\bcomments\x18\x02 \x03(\v2\x14.ermite.IssueCommentR\bcomments\"X\n" +
1407+ "\x16AddIssueCommentRequest\x12\x12\n" +
1408+ "\x04repo\x18\x01 \x01(\tR\x04repo\x12\x16\n" +
1409+ "\x06number\x18\x02 \x01(\x03R\x06number\x12\x12\n" +
1410+ "\x04text\x18\x03 \x01(\tR\x04text\"I\n" +
1411+ "\x17AddIssueCommentResponse\x12.\n" +
1412+ "\acomment\x18\x01 \x01(\v2\x14.ermite.IssueCommentR\acomment\"?\n" +
1413+ "\x11CloseIssueRequest\x12\x12\n" +
1414+ "\x04repo\x18\x01 \x01(\tR\x04repo\x12\x16\n" +
1415+ "\x06number\x18\x02 \x01(\x03R\x06number\"9\n" +
1416+ "\x12CloseIssueResponse\x12#\n" +
1417+ "\x05issue\x18\x01 \x01(\v2\r.ermite.IssueR\x05issue\"@\n" +
1418+ "\x12ReopenIssueRequest\x12\x12\n" +
1419+ "\x04repo\x18\x01 \x01(\tR\x04repo\x12\x16\n" +
1420+ "\x06number\x18\x02 \x01(\x03R\x06number\":\n" +
1421+ "\x13ReopenIssueResponse\x12#\n" +
1422+ "\x05issue\x18\x01 \x01(\v2\r.ermite.IssueR\x05issue\"\xaf\x01\n" +
1423+ "\fPendingIssue\x12\x0e\n" +
1424+ "\x02id\x18\x01 \x01(\tR\x02id\x12\x12\n" +
1425+ "\x04repo\x18\x02 \x01(\tR\x04repo\x12\x14\n" +
1426+ "\x05title\x18\x03 \x01(\tR\x05title\x12\x12\n" +
1427+ "\x04body\x18\x04 \x01(\tR\x04body\x12\x16\n" +
1428+ "\x06author\x18\x05 \x01(\tR\x06author\x129\n" +
1429+ "\n" +
1430+ "created_at\x18\x06 \x01(\v2\x1a.google.protobuf.TimestampR\tcreatedAt\"j\n" +
1431+ "\x18ListPendingIssuesRequest\x12\x12\n" +
1432+ "\x04repo\x18\x01 \x01(\tR\x04repo\x12\x1b\n" +
1433+ "\tpage_size\x18\x02 \x01(\x05R\bpageSize\x12\x1d\n" +
1434+ "\n" +
1435+ "page_token\x18\x03 \x01(\tR\tpageToken\"q\n" +
1436+ "\x19ListPendingIssuesResponse\x12,\n" +
1437+ "\x06issues\x18\x01 \x03(\v2\x14.ermite.PendingIssueR\x06issues\x12&\n" +
1438+ "\x0fnext_page_token\x18\x02 \x01(\tR\rnextPageToken\"9\n" +
1439+ "\x13ApproveIssueRequest\x12\x12\n" +
1440+ "\x04repo\x18\x01 \x01(\tR\x04repo\x12\x0e\n" +
1441+ "\x02id\x18\x02 \x01(\tR\x02id\";\n" +
1442+ "\x14ApproveIssueResponse\x12#\n" +
1443+ "\x05issue\x18\x01 \x01(\v2\r.ermite.IssueR\x05issue\"J\n" +
1444+ "\x12RejectIssueRequest\x12\x12\n" +
1445+ "\x04repo\x18\x01 \x01(\tR\x04repo\x12\x0e\n" +
1446+ "\x02id\x18\x02 \x01(\tR\x02id\x12\x10\n" +
1447+ "\x03ban\x18\x03 \x01(\bR\x03ban\"\x15\n" +
1448+ "\x13RejectIssueResponse*p\n" +
1449+ "\n" +
1450+ "IssueState\x12\x1b\n" +
1451+ "\x17ISSUE_STATE_UNSPECIFIED\x10\x00\x12\x14\n" +
1452+ "\x10ISSUE_STATE_OPEN\x10\x01\x12\x16\n" +
1453+ "\x12ISSUE_STATE_CLOSED\x10\x02\x12\x17\n" +
1454+ "\x13ISSUE_STATE_PENDING\x10\x032\xa8\x05\n" +
1455+ "\fIssueService\x12F\n" +
1456+ "\vCreateIssue\x12\x1a.ermite.CreateIssueRequest\x1a\x1b.ermite.CreateIssueResponse\x12C\n" +
1457+ "\n" +
1458+ "ListIssues\x12\x19.ermite.ListIssuesRequest\x1a\x1a.ermite.ListIssuesResponse\x12=\n" +
1459+ "\bGetIssue\x12\x17.ermite.GetIssueRequest\x1a\x18.ermite.GetIssueResponse\x12R\n" +
1460+ "\x0fAddIssueComment\x12\x1e.ermite.AddIssueCommentRequest\x1a\x1f.ermite.AddIssueCommentResponse\x12C\n" +
1461+ "\n" +
1462+ "CloseIssue\x12\x19.ermite.CloseIssueRequest\x1a\x1a.ermite.CloseIssueResponse\x12F\n" +
1463+ "\vReopenIssue\x12\x1a.ermite.ReopenIssueRequest\x1a\x1b.ermite.ReopenIssueResponse\x12X\n" +
1464+ "\x11ListPendingIssues\x12 .ermite.ListPendingIssuesRequest\x1a!.ermite.ListPendingIssuesResponse\x12I\n" +
1465+ "\fApproveIssue\x12\x1b.ermite.ApproveIssueRequest\x1a\x1c.ermite.ApproveIssueResponse\x12F\n" +
1466+ "\vRejectIssue\x12\x1a.ermite.RejectIssueRequest\x1a\x1b.ermite.RejectIssueResponseBq\n" +
1467+ "\n" +
1468+ "com.ermiteB\n" +
1469+ "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"
1470+
1471+var (
1472+ file_ermite_issue_proto_rawDescOnce sync.Once
1473+ file_ermite_issue_proto_rawDescData []byte
1474+)
1475+
1476+func file_ermite_issue_proto_rawDescGZIP() []byte {
1477+ file_ermite_issue_proto_rawDescOnce.Do(func() {
1478+ file_ermite_issue_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_ermite_issue_proto_rawDesc), len(file_ermite_issue_proto_rawDesc)))
1479+ })
1480+ return file_ermite_issue_proto_rawDescData
1481+}
1482+
1483+var file_ermite_issue_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
1484+var file_ermite_issue_proto_msgTypes = make([]protoimpl.MessageInfo, 21)
1485+var file_ermite_issue_proto_goTypes = []any{
1486+ (IssueState)(0), // 0: ermite.IssueState
1487+ (*Issue)(nil), // 1: ermite.Issue
1488+ (*IssueComment)(nil), // 2: ermite.IssueComment
1489+ (*CreateIssueRequest)(nil), // 3: ermite.CreateIssueRequest
1490+ (*CreateIssueResponse)(nil), // 4: ermite.CreateIssueResponse
1491+ (*ListIssuesRequest)(nil), // 5: ermite.ListIssuesRequest
1492+ (*ListIssuesResponse)(nil), // 6: ermite.ListIssuesResponse
1493+ (*GetIssueRequest)(nil), // 7: ermite.GetIssueRequest
1494+ (*GetIssueResponse)(nil), // 8: ermite.GetIssueResponse
1495+ (*AddIssueCommentRequest)(nil), // 9: ermite.AddIssueCommentRequest
1496+ (*AddIssueCommentResponse)(nil), // 10: ermite.AddIssueCommentResponse
1497+ (*CloseIssueRequest)(nil), // 11: ermite.CloseIssueRequest
1498+ (*CloseIssueResponse)(nil), // 12: ermite.CloseIssueResponse
1499+ (*ReopenIssueRequest)(nil), // 13: ermite.ReopenIssueRequest
1500+ (*ReopenIssueResponse)(nil), // 14: ermite.ReopenIssueResponse
1501+ (*PendingIssue)(nil), // 15: ermite.PendingIssue
1502+ (*ListPendingIssuesRequest)(nil), // 16: ermite.ListPendingIssuesRequest
1503+ (*ListPendingIssuesResponse)(nil), // 17: ermite.ListPendingIssuesResponse
1504+ (*ApproveIssueRequest)(nil), // 18: ermite.ApproveIssueRequest
1505+ (*ApproveIssueResponse)(nil), // 19: ermite.ApproveIssueResponse
1506+ (*RejectIssueRequest)(nil), // 20: ermite.RejectIssueRequest
1507+ (*RejectIssueResponse)(nil), // 21: ermite.RejectIssueResponse
1508+ (*timestamppb.Timestamp)(nil), // 22: google.protobuf.Timestamp
1509+}
1510+var file_ermite_issue_proto_depIdxs = []int32{
1511+ 0, // 0: ermite.Issue.state:type_name -> ermite.IssueState
1512+ 22, // 1: ermite.Issue.created_at:type_name -> google.protobuf.Timestamp
1513+ 22, // 2: ermite.Issue.updated_at:type_name -> google.protobuf.Timestamp
1514+ 22, // 3: ermite.IssueComment.created_at:type_name -> google.protobuf.Timestamp
1515+ 1, // 4: ermite.CreateIssueResponse.issue:type_name -> ermite.Issue
1516+ 0, // 5: ermite.ListIssuesRequest.state:type_name -> ermite.IssueState
1517+ 1, // 6: ermite.ListIssuesResponse.issues:type_name -> ermite.Issue
1518+ 1, // 7: ermite.GetIssueResponse.issue:type_name -> ermite.Issue
1519+ 2, // 8: ermite.GetIssueResponse.comments:type_name -> ermite.IssueComment
1520+ 2, // 9: ermite.AddIssueCommentResponse.comment:type_name -> ermite.IssueComment
1521+ 1, // 10: ermite.CloseIssueResponse.issue:type_name -> ermite.Issue
1522+ 1, // 11: ermite.ReopenIssueResponse.issue:type_name -> ermite.Issue
1523+ 22, // 12: ermite.PendingIssue.created_at:type_name -> google.protobuf.Timestamp
1524+ 15, // 13: ermite.ListPendingIssuesResponse.issues:type_name -> ermite.PendingIssue
1525+ 1, // 14: ermite.ApproveIssueResponse.issue:type_name -> ermite.Issue
1526+ 3, // 15: ermite.IssueService.CreateIssue:input_type -> ermite.CreateIssueRequest
1527+ 5, // 16: ermite.IssueService.ListIssues:input_type -> ermite.ListIssuesRequest
1528+ 7, // 17: ermite.IssueService.GetIssue:input_type -> ermite.GetIssueRequest
1529+ 9, // 18: ermite.IssueService.AddIssueComment:input_type -> ermite.AddIssueCommentRequest
1530+ 11, // 19: ermite.IssueService.CloseIssue:input_type -> ermite.CloseIssueRequest
1531+ 13, // 20: ermite.IssueService.ReopenIssue:input_type -> ermite.ReopenIssueRequest
1532+ 16, // 21: ermite.IssueService.ListPendingIssues:input_type -> ermite.ListPendingIssuesRequest
1533+ 18, // 22: ermite.IssueService.ApproveIssue:input_type -> ermite.ApproveIssueRequest
1534+ 20, // 23: ermite.IssueService.RejectIssue:input_type -> ermite.RejectIssueRequest
1535+ 4, // 24: ermite.IssueService.CreateIssue:output_type -> ermite.CreateIssueResponse
1536+ 6, // 25: ermite.IssueService.ListIssues:output_type -> ermite.ListIssuesResponse
1537+ 8, // 26: ermite.IssueService.GetIssue:output_type -> ermite.GetIssueResponse
1538+ 10, // 27: ermite.IssueService.AddIssueComment:output_type -> ermite.AddIssueCommentResponse
1539+ 12, // 28: ermite.IssueService.CloseIssue:output_type -> ermite.CloseIssueResponse
1540+ 14, // 29: ermite.IssueService.ReopenIssue:output_type -> ermite.ReopenIssueResponse
1541+ 17, // 30: ermite.IssueService.ListPendingIssues:output_type -> ermite.ListPendingIssuesResponse
1542+ 19, // 31: ermite.IssueService.ApproveIssue:output_type -> ermite.ApproveIssueResponse
1543+ 21, // 32: ermite.IssueService.RejectIssue:output_type -> ermite.RejectIssueResponse
1544+ 24, // [24:33] is the sub-list for method output_type
1545+ 15, // [15:24] is the sub-list for method input_type
1546+ 15, // [15:15] is the sub-list for extension type_name
1547+ 15, // [15:15] is the sub-list for extension extendee
1548+ 0, // [0:15] is the sub-list for field type_name
1549+}
1550+
1551+func init() { file_ermite_issue_proto_init() }
1552+func file_ermite_issue_proto_init() {
1553+ if File_ermite_issue_proto != nil {
1554+ return
1555+ }
1556+ type x struct{}
1557+ out := protoimpl.TypeBuilder{
1558+ File: protoimpl.DescBuilder{
1559+ GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
1560+ RawDescriptor: unsafe.Slice(unsafe.StringData(file_ermite_issue_proto_rawDesc), len(file_ermite_issue_proto_rawDesc)),
1561+ NumEnums: 1,
1562+ NumMessages: 21,
1563+ NumExtensions: 0,
1564+ NumServices: 1,
1565+ },
1566+ GoTypes: file_ermite_issue_proto_goTypes,
1567+ DependencyIndexes: file_ermite_issue_proto_depIdxs,
1568+ EnumInfos: file_ermite_issue_proto_enumTypes,
1569+ MessageInfos: file_ermite_issue_proto_msgTypes,
1570+ }.Build()
1571+ File_ermite_issue_proto = out.File
1572+ file_ermite_issue_proto_goTypes = nil
1573+ file_ermite_issue_proto_depIdxs = nil
1574+}
M gen/ermite/repo.pb.go +151 -57 generated file: collapsed
M gen/ermite/token.pb.go +8 -2 generated file: collapsed
A issue_test.go +98 -0
@@ -0,0 +1,98 @@
1+// SPDX-License-Identifier: MIT
2+
3+package api_test
4+
5+import (
6+ "testing"
7+
8+ "bnema.dev/ermite-api/gen/ermite"
9+ "bnema.dev/ermite-api/gen/ermite/ermiteconnect"
10+ "google.golang.org/protobuf/proto"
11+ "google.golang.org/protobuf/reflect/protoreflect"
12+)
13+
14+func TestIssueProcedures(t *testing.T) {
15+ 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",
22+ ermiteconnect.IssueServiceListPendingIssuesProcedure: "ListPendingIssues",
23+ ermiteconnect.IssueServiceApproveIssueProcedure: "ApproveIssue",
24+ ermiteconnect.IssueServiceRejectIssueProcedure: "RejectIssue",
25+ }
26+ for got, name := range tests {
27+ if want := "/ermite.IssueService/" + name; got != want {
28+ t.Errorf("procedure = %q, want %q", got, want)
29+ }
30+ }
31+}
32+
33+func TestIssueContract(t *testing.T) {
34+ for _, tt := range []struct {
35+ msg proto.Message
36+ fields map[string]int
37+ }{
38+ {&ermite.Issue{}, map[string]int{"repo": 1, "number": 2, "title": 3, "body": 4, "state": 5, "author": 6, "created_at": 7, "updated_at": 8, "comment_count": 9}},
39+ {&ermite.IssueComment{}, map[string]int{"id": 1, "author": 2, "text": 3, "created_at": 4}},
40+ {&ermite.RepoSettings{}, map[string]int{"issues": 3}},
41+ {&ermite.UpdateRepoRequest{}, map[string]int{"issues": 8}},
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}},
50+ } {
51+ md := tt.msg.ProtoReflect().Descriptor()
52+ for name, num := range tt.fields {
53+ if fd := md.Fields().ByName(protoreflect.Name(name)); fd == nil || int(fd.Number()) != num {
54+ t.Errorf("%s.%s = %v, want %d", md.Name(), name, fd, num)
55+ }
56+ }
57+ }
58+ if ermite.Scope_SCOPE_ISSUE_WRITE != 11 {
59+ t.Fatal("issue:write scope number changed")
60+ }
61+ for name, want := range map[string]int32{"ISSUE_STATE_UNSPECIFIED": 0, "ISSUE_STATE_OPEN": 1, "ISSUE_STATE_CLOSED": 2, "ISSUE_STATE_PENDING": 3} {
62+ if got := ermite.IssueState_value[name]; got != want {
63+ t.Errorf("%s = %d, want %d", name, got, want)
64+ }
65+ }
66+ for name, want := range map[string]int32{"ISSUES_MODE_UNSPECIFIED": 0, "ISSUES_MODE_OFF": 1, "ISSUES_MODE_OWNER": 2, "ISSUES_MODE_PUBLIC": 3} {
67+ if got := ermite.IssuesMode_value[name]; got != want {
68+ t.Errorf("%s = %d, want %d", name, got, want)
69+ }
70+ }
71+ mode := ermite.IssuesMode_ISSUES_MODE_OFF
72+ for _, in := range []proto.Message{
73+ &ermite.UpdateRepoRequest{Repo: "r", Issues: &mode},
74+ &ermite.Event{Kind: &ermite.Event_IssueOpened{IssueOpened: &ermite.IssueOpened{Issue: &ermite.Issue{Repo: "r", Number: 1}}}},
75+ &ermite.Event{Kind: &ermite.Event_IssueClosed{IssueClosed: &ermite.IssueClosed{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},
80+ &ermite.Event{Kind: &ermite.Event_IssueCommented{IssueCommented: &ermite.IssueCommented{Repo: "r", Number: 1, Comment: &ermite.IssueComment{Id: "c", Text: "hi"}}}},
81+ } {
82+ b, err := proto.Marshal(in)
83+ if err != nil {
84+ t.Fatal(err)
85+ }
86+ out := in.ProtoReflect().New().Interface()
87+ if err := proto.Unmarshal(b, out); err != nil {
88+ t.Fatal(err)
89+ }
90+ if !proto.Equal(in, out) {
91+ t.Errorf("%T: round trip mismatch", in)
92+ }
93+ }
94+ fd := (&ermite.UpdateRepoRequest{}).ProtoReflect().Descriptor().Fields().ByName("issues")
95+ if !fd.HasOptionalKeyword() {
96+ t.Fatal("UpdateRepoRequest.issues must be optional")
97+ }
98+}
@@ -0,0 +1,98 @@
1+// SPDX-License-Identifier: MIT
2+
3+package api_test
4+
5+import (
6+ "testing"
7+
8+ "bnema.dev/ermite-api/gen/ermite"
9+ "bnema.dev/ermite-api/gen/ermite/ermiteconnect"
10+ "google.golang.org/protobuf/proto"
11+ "google.golang.org/protobuf/reflect/protoreflect"
12+)
13+
14+func TestIssueProcedures(t *testing.T) {
15+ 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",
22+ ermiteconnect.IssueServiceListPendingIssuesProcedure: "ListPendingIssues",
23+ ermiteconnect.IssueServiceApproveIssueProcedure: "ApproveIssue",
24+ ermiteconnect.IssueServiceRejectIssueProcedure: "RejectIssue",
25+ }
26+ for got, name := range tests {
27+ if want := "/ermite.IssueService/" + name; got != want {
28+ t.Errorf("procedure = %q, want %q", got, want)
29+ }
30+ }
31+}
32+
33+func TestIssueContract(t *testing.T) {
34+ for _, tt := range []struct {
35+ msg proto.Message
36+ fields map[string]int
37+ }{
38+ {&ermite.Issue{}, map[string]int{"repo": 1, "number": 2, "title": 3, "body": 4, "state": 5, "author": 6, "created_at": 7, "updated_at": 8, "comment_count": 9}},
39+ {&ermite.IssueComment{}, map[string]int{"id": 1, "author": 2, "text": 3, "created_at": 4}},
40+ {&ermite.RepoSettings{}, map[string]int{"issues": 3}},
41+ {&ermite.UpdateRepoRequest{}, map[string]int{"issues": 8}},
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}},
50+ } {
51+ md := tt.msg.ProtoReflect().Descriptor()
52+ for name, num := range tt.fields {
53+ if fd := md.Fields().ByName(protoreflect.Name(name)); fd == nil || int(fd.Number()) != num {
54+ t.Errorf("%s.%s = %v, want %d", md.Name(), name, fd, num)
55+ }
56+ }
57+ }
58+ if ermite.Scope_SCOPE_ISSUE_WRITE != 11 {
59+ t.Fatal("issue:write scope number changed")
60+ }
61+ for name, want := range map[string]int32{"ISSUE_STATE_UNSPECIFIED": 0, "ISSUE_STATE_OPEN": 1, "ISSUE_STATE_CLOSED": 2, "ISSUE_STATE_PENDING": 3} {
62+ if got := ermite.IssueState_value[name]; got != want {
63+ t.Errorf("%s = %d, want %d", name, got, want)
64+ }
65+ }
66+ for name, want := range map[string]int32{"ISSUES_MODE_UNSPECIFIED": 0, "ISSUES_MODE_OFF": 1, "ISSUES_MODE_OWNER": 2, "ISSUES_MODE_PUBLIC": 3} {
67+ if got := ermite.IssuesMode_value[name]; got != want {
68+ t.Errorf("%s = %d, want %d", name, got, want)
69+ }
70+ }
71+ mode := ermite.IssuesMode_ISSUES_MODE_OFF
72+ for _, in := range []proto.Message{
73+ &ermite.UpdateRepoRequest{Repo: "r", Issues: &mode},
74+ &ermite.Event{Kind: &ermite.Event_IssueOpened{IssueOpened: &ermite.IssueOpened{Issue: &ermite.Issue{Repo: "r", Number: 1}}}},
75+ &ermite.Event{Kind: &ermite.Event_IssueClosed{IssueClosed: &ermite.IssueClosed{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},
80+ &ermite.Event{Kind: &ermite.Event_IssueCommented{IssueCommented: &ermite.IssueCommented{Repo: "r", Number: 1, Comment: &ermite.IssueComment{Id: "c", Text: "hi"}}}},
81+ } {
82+ b, err := proto.Marshal(in)
83+ if err != nil {
84+ t.Fatal(err)
85+ }
86+ out := in.ProtoReflect().New().Interface()
87+ if err := proto.Unmarshal(b, out); err != nil {
88+ t.Fatal(err)
89+ }
90+ if !proto.Equal(in, out) {
91+ t.Errorf("%T: round trip mismatch", in)
92+ }
93+ }
94+ fd := (&ermite.UpdateRepoRequest{}).ProtoReflect().Descriptor().Fields().ByName("issues")
95+ if !fd.HasOptionalKeyword() {
96+ t.Fatal("UpdateRepoRequest.issues must be optional")
97+ }
98+}
M proto/ermite/event.proto +69 -7
@@ -4,6 +4,7 @@ syntax = "proto3";
44
55 package ermite;
66
7+import "ermite/issue.proto";
78 import "ermite/merge_request.proto";
89 import "ermite/repo.proto";
910 import "ermite/run.proto";
@@ -18,9 +19,12 @@ service EventService {
1819 // The events are repo_created, repo_updated, repo_deleted, pushed, gap and,
1920 // for merge requests, merge_request_opened, merge_request_merged,
2021 // merge_request_closed, comment_added, comment_edited, comment_deleted and
21- // comment_resolved (a thread resolved or reopened). Each one is delivered on the
22- // "repo:<name>" topic of its repository and on "home", subject to the
23- // visibility of the repository.
22+ // comment_resolved (a thread resolved or reopened); for issues, issue_opened,
23+ // issue_closed, issue_reopened and issue_commented, and issue_submitted. Each
24+ // one is delivered on the "repo:<name>" topic of its repository and on
25+ // "home", subject to the visibility of the repository, except
26+ // issue_submitted: it is delivered only on the "owner" topic, never on
27+ // "home" or "repo:<name>", and carries no content (see IssueSubmitted).
2428 //
2529 // Servers with the feature "ci.runs" also publish run_updated and
2630 // job_updated, only on the "repo:<name>" topic of their repository (never on
@@ -55,9 +59,17 @@ service EventService {
5559 // WatchRequest selects the events to receive.
5660 message WatchRequest {
5761 // Topics are the subjects to follow: "home" for every repository visible to
58- // the caller, or "repo:<name>" for one repository. Empty means "home".
59- // Unknown topics are invalid_argument. A "repo:<name>" topic for a repository
60- // that does not exist or is not visible is not_found.
62+ // the caller, "repo:<name>" for one repository, or "owner". Empty means
63+ // "home". Unknown topics are invalid_argument. A "repo:<name>" topic for a
64+ // repository that does not exist or is not visible is not_found.
65+ //
66+ // The "owner" topic needs owner rights: root, an owner session, or a token
67+ // that holds every scope with no repository or branch limit. Anyone else
68+ // gets the same invalid_argument as for an unknown topic, so the topic is
69+ // not revealed. It carries the merge request and issue events of every
70+ // repository (merge_request_opened, merge_request_merged,
71+ // merge_request_closed, comment_added, and the issue events), repo_updated
72+ // and repo_deleted, plus issue_submitted, which is delivered nowhere else.
6173 repeated string topics = 1;
6274 // ResumeAfter is the Event.id of the last event the client has seen. Empty
6375 // to receive only new events.
@@ -109,6 +121,17 @@ message Event {
109121 CommentDeleted comment_deleted = 15;
110122 // CommentResolved: a merge request comment was resolved or unresolved.
111123 CommentResolved comment_resolved = 16;
124+ // IssueOpened: an issue was opened.
125+ IssueOpened issue_opened = 17;
126+ // IssueClosed: an issue was closed.
127+ IssueClosed issue_closed = 18;
128+ // IssueReopened: an issue was reopened.
129+ IssueReopened issue_reopened = 19;
130+ // IssueCommented: a comment was added to an issue.
131+ IssueCommented issue_commented = 20;
132+ // IssueSubmitted: an issue is awaiting moderation. Delivered only on the
133+ // "owner" topic, never on "home" or "repo:<name>", and carries no content.
134+ IssueSubmitted issue_submitted = 21;
112135 }
113136 }
114137
@@ -209,10 +232,49 @@ message CommentResolved {
209232 Comment comment = 3;
210233 }
211234
235+// IssueOpened reports a new issue.
236+message IssueOpened {
237+ // Issue is the new issue, including its repository and number.
238+ Issue issue = 1;
239+}
240+
241+// IssueClosed reports a closed issue.
242+message IssueClosed {
243+ // Issue is the issue after closing, including its repository and number.
244+ Issue issue = 1;
245+}
246+
247+// IssueReopened reports a reopened issue.
248+message IssueReopened {
249+ // Issue is the issue after reopening, including its repository and number.
250+ Issue issue = 1;
251+}
252+
253+// IssueCommented reports a new comment on an issue.
254+message IssueCommented {
255+ // Repo is the name of the repository.
256+ string repo = 1;
257+ // Number is the number of the issue.
258+ int64 number = 2;
259+ // Comment is the new comment.
260+ IssueComment comment = 3;
261+}
262+
263+// IssueSubmitted reports an issue awaiting moderation. It is delivered only
264+// on the "owner" topic, never on "home" or "repo:<name>". It carries no title or body: content never travels in
265+// events, read it with IssueService.ListPendingIssues.
266+message IssueSubmitted {
267+ // Repo is the name of the repository.
268+ string repo = 1;
269+ // Id is the PendingIssue.id.
270+ string id = 2;
271+}
272+
212273 // Gap tells the client that events were lost: the requested resume point is
213274 // older than the server's buffer. It has no fields. The client should reload
214275 // the state it displays (repositories, refs, merge requests and their
215-// comments, CI runs and their jobs) and carry on with the next events.
276+// comments, issues and their comments, CI runs and their jobs) and carry on
277+// with the next events.
216278 message Gap {}
217279
218280 // RunUpdated reports a CI run that was created or whose status, conclusion or
@@ -4,6 +4,7 @@ syntax = "proto3";
4 4
5 package ermite;5 package ermite;
6 6
7+import "ermite/issue.proto";
7 import "ermite/merge_request.proto";8 import "ermite/merge_request.proto";
8 import "ermite/repo.proto";9 import "ermite/repo.proto";
9 import "ermite/run.proto";10 import "ermite/run.proto";
@@ -18,9 +19,12 @@ service EventService {
18 // The events are repo_created, repo_updated, repo_deleted, pushed, gap and,19 // The events are repo_created, repo_updated, repo_deleted, pushed, gap and,
19 // for merge requests, merge_request_opened, merge_request_merged,20 // for merge requests, merge_request_opened, merge_request_merged,
20 // merge_request_closed, comment_added, comment_edited, comment_deleted and21 // merge_request_closed, comment_added, comment_edited, comment_deleted and
21- // comment_resolved (a thread resolved or reopened). Each one is delivered on the22+ // comment_resolved (a thread resolved or reopened); for issues, issue_opened,
22- // "repo:<name>" topic of its repository and on "home", subject to the23+ // issue_closed, issue_reopened and issue_commented, and issue_submitted. Each
23- // visibility of the repository.24+ // one is delivered on the "repo:<name>" topic of its repository and on
25+ // "home", subject to the visibility of the repository, except
26+ // issue_submitted: it is delivered only on the "owner" topic, never on
27+ // "home" or "repo:<name>", and carries no content (see IssueSubmitted).
24 //28 //
25 // Servers with the feature "ci.runs" also publish run_updated and29 // Servers with the feature "ci.runs" also publish run_updated and
26 // job_updated, only on the "repo:<name>" topic of their repository (never on30 // job_updated, only on the "repo:<name>" topic of their repository (never on
@@ -55,9 +59,17 @@ service EventService {
55 // WatchRequest selects the events to receive.59 // WatchRequest selects the events to receive.
56 message WatchRequest {60 message WatchRequest {
57 // Topics are the subjects to follow: "home" for every repository visible to61 // Topics are the subjects to follow: "home" for every repository visible to
58- // the caller, or "repo:<name>" for one repository. Empty means "home".62+ // the caller, "repo:<name>" for one repository, or "owner". Empty means
59- // Unknown topics are invalid_argument. A "repo:<name>" topic for a repository63+ // "home". Unknown topics are invalid_argument. A "repo:<name>" topic for a
60- // that does not exist or is not visible is not_found.64+ // repository that does not exist or is not visible is not_found.
65+ //
66+ // The "owner" topic needs owner rights: root, an owner session, or a token
67+ // that holds every scope with no repository or branch limit. Anyone else
68+ // gets the same invalid_argument as for an unknown topic, so the topic is
69+ // not revealed. It carries the merge request and issue events of every
70+ // repository (merge_request_opened, merge_request_merged,
71+ // merge_request_closed, comment_added, and the issue events), repo_updated
72+ // and repo_deleted, plus issue_submitted, which is delivered nowhere else.
61 repeated string topics = 1;73 repeated string topics = 1;
62 // ResumeAfter is the Event.id of the last event the client has seen. Empty74 // ResumeAfter is the Event.id of the last event the client has seen. Empty
63 // to receive only new events.75 // to receive only new events.
@@ -109,6 +121,17 @@ message Event {
109 CommentDeleted comment_deleted = 15;121 CommentDeleted comment_deleted = 15;
110 // CommentResolved: a merge request comment was resolved or unresolved.122 // CommentResolved: a merge request comment was resolved or unresolved.
111 CommentResolved comment_resolved = 16;123 CommentResolved comment_resolved = 16;
124+ // IssueOpened: an issue was opened.
125+ IssueOpened issue_opened = 17;
126+ // IssueClosed: an issue was closed.
127+ IssueClosed issue_closed = 18;
128+ // IssueReopened: an issue was reopened.
129+ IssueReopened issue_reopened = 19;
130+ // IssueCommented: a comment was added to an issue.
131+ IssueCommented issue_commented = 20;
132+ // IssueSubmitted: an issue is awaiting moderation. Delivered only on the
133+ // "owner" topic, never on "home" or "repo:<name>", and carries no content.
134+ IssueSubmitted issue_submitted = 21;
112 }135 }
113 }136 }
114 137
@@ -209,10 +232,49 @@ message CommentResolved {
209 Comment comment = 3;232 Comment comment = 3;
210 }233 }
211 234
235+// IssueOpened reports a new issue.
236+message IssueOpened {
237+ // Issue is the new issue, including its repository and number.
238+ Issue issue = 1;
239+}
240+
241+// IssueClosed reports a closed issue.
242+message IssueClosed {
243+ // Issue is the issue after closing, including its repository and number.
244+ Issue issue = 1;
245+}
246+
247+// IssueReopened reports a reopened issue.
248+message IssueReopened {
249+ // Issue is the issue after reopening, including its repository and number.
250+ Issue issue = 1;
251+}
252+
253+// IssueCommented reports a new comment on an issue.
254+message IssueCommented {
255+ // Repo is the name of the repository.
256+ string repo = 1;
257+ // Number is the number of the issue.
258+ int64 number = 2;
259+ // Comment is the new comment.
260+ IssueComment comment = 3;
261+}
262+
263+// IssueSubmitted reports an issue awaiting moderation. It is delivered only
264+// on the "owner" topic, never on "home" or "repo:<name>". It carries no title or body: content never travels in
265+// events, read it with IssueService.ListPendingIssues.
266+message IssueSubmitted {
267+ // Repo is the name of the repository.
268+ string repo = 1;
269+ // Id is the PendingIssue.id.
270+ string id = 2;
271+}
272+
212 // Gap tells the client that events were lost: the requested resume point is273 // Gap tells the client that events were lost: the requested resume point is
213 // older than the server's buffer. It has no fields. The client should reload274 // older than the server's buffer. It has no fields. The client should reload
214 // the state it displays (repositories, refs, merge requests and their275 // the state it displays (repositories, refs, merge requests and their
215-// comments, CI runs and their jobs) and carry on with the next events.276+// comments, issues and their comments, CI runs and their jobs) and carry on
277+// with the next events.
216 message Gap {}278 message Gap {}
217 279
218 // RunUpdated reports a CI run that was created or whose status, conclusion or280 // RunUpdated reports a CI run that was created or whose status, conclusion or
A proto/ermite/issue.proto +281 -0
@@ -0,0 +1,281 @@
1+// SPDX-License-Identifier: MIT
2+
3+syntax = "proto3";
4+
5+package ermite;
6+
7+import "google/protobuf/timestamp.proto";
8+
9+// IssueState is where an issue is in its life.
10+enum IssueState {
11+ // Not set. In ListIssuesRequest.state it means every state. Never returned
12+ // by the server.
13+ ISSUE_STATE_UNSPECIFIED = 0;
14+ // The issue is open.
15+ ISSUE_STATE_OPEN = 1;
16+ // The issue is closed.
17+ ISSUE_STATE_CLOSED = 2;
18+ // Reserved for later use: awaiting moderation, owner-only.
19+ ISSUE_STATE_PENDING = 3;
20+}
21+
22+// Issue is a discussion in a repository.
23+message Issue {
24+ // Repo is the name of the repository.
25+ string repo = 1;
26+ // Number identifies the issue within its repository.
27+ int64 number = 2;
28+ // Title is the one-line title.
29+ string title = 3;
30+ // Body is the content of the issue. It may be empty.
31+ string body = 4;
32+ // State is where the issue is in its life.
33+ IssueState state = 5;
34+ // Author names who created the issue: the label of the token or session
35+ // that was used ("root" for the root token, "owner" for a web session), or,
36+ // for an issue approved from the public form, the unverified display name
37+ // the visitor typed (or "anonymous"). It is informational and never proves
38+ // identity: labels are not unique, the token may be gone, and a visitor can
39+ // type "owner".
40+ string author = 6;
41+ // CreatedAt is when the issue was created.
42+ google.protobuf.Timestamp created_at = 7;
43+ // UpdatedAt is when the issue last changed.
44+ google.protobuf.Timestamp updated_at = 8;
45+ // CommentCount is the number of comments on the issue.
46+ int32 comment_count = 9;
47+}
48+
49+// IssueComment is a note on an issue.
50+message IssueComment {
51+ // Id identifies the comment within its issue. Opaque.
52+ string id = 1;
53+ // Author names who wrote the comment, like Issue.author.
54+ string author = 2;
55+ // Text is the content of the comment.
56+ string text = 3;
57+ // CreatedAt is when the comment was written.
58+ google.protobuf.Timestamp created_at = 4;
59+}
60+
61+// IssueService manages issues and their comments.
62+//
63+// Servers that implement this service list "issues" in
64+// GetVersionResponse.features. Issues and merge requests of a repository share
65+// one number sequence. When the issues mode of the repository is off, every
66+// call answers not_found, as for a repository that does not exist.
67+//
68+// Reading (ListIssues, GetIssue) needs the same access as reading the
69+// repository: nothing for a public one, the repo:read scope for a private one.
70+// Writing (CreateIssue, AddIssueComment, CloseIssue, ReopenIssue) needs the
71+// owner or a token with the issue:write scope on the repository; branch
72+// prefixes do not restrict it. A caller without credentials gets
73+// unauthenticated, one with an insufficient scope gets permission_denied, and
74+// one that cannot read the repository gets not_found.
75+//
76+// Text fields are normalized to NFC and stripped of control and Unicode
77+// bidirectional characters (line breaks and tabs stay in bodies and comments).
78+// The title is one line of 1 to 120 code points; body and comment text are at
79+// most 10,000 code points and a comment must not be blank. A text that breaks
80+// a rule is rejected with invalid_argument. A comment on an issue that already
81+// holds 1,000 comments gives resource_exhausted. Closing an issue that is not
82+// open, or reopening one that is not closed, gives failed_precondition.
83+//
84+// The ordinary RPCs only return issues in state OPEN or CLOSED, and
85+// ListIssues with state ISSUE_STATE_PENDING is invalid_argument. Submissions
86+// from the public form wait as PendingIssue records, reachable only through
87+// the three moderation RPCs (ListPendingIssues, ApproveIssue, RejectIssue).
88+// These need owner rights (root, an owner session, or a token holding every
89+// scope with no repository or branch limit); anyone else gets not_found,
90+// whatever the request holds. For the owner, a malformed pending issue id is
91+// not_found and a malformed repository name is invalid_argument.
92+service IssueService {
93+ // CreateIssue opens an issue. Needs the issue:write scope.
94+ rpc CreateIssue(CreateIssueRequest) returns (CreateIssueResponse);
95+ // ListIssues lists issues, highest number first, optionally in one state.
96+ rpc ListIssues(ListIssuesRequest) returns (ListIssuesResponse);
97+ // GetIssue returns an issue with its comments.
98+ rpc GetIssue(GetIssueRequest) returns (GetIssueResponse);
99+ // AddIssueComment adds a comment. Needs the issue:write scope.
100+ rpc AddIssueComment(AddIssueCommentRequest) returns (AddIssueCommentResponse);
101+ // CloseIssue closes an issue. Needs the issue:write scope.
102+ rpc CloseIssue(CloseIssueRequest) returns (CloseIssueResponse);
103+ // ReopenIssue reopens an issue. Needs the issue:write scope.
104+ rpc ReopenIssue(ReopenIssueRequest) returns (ReopenIssueResponse);
105+ // ListPendingIssues lists issues awaiting moderation, oldest first. Owner
106+ // rights only; anyone else gets NOT_FOUND.
107+ rpc ListPendingIssues(ListPendingIssuesRequest) returns (ListPendingIssuesResponse);
108+ // ApproveIssue turns a pending issue into an open issue with a new number.
109+ // Owner rights only; anyone else gets NOT_FOUND.
110+ rpc ApproveIssue(ApproveIssueRequest) returns (ApproveIssueResponse);
111+ // RejectIssue discards a pending issue, optionally banning its submitter.
112+ // Owner rights only; anyone else gets NOT_FOUND.
113+ rpc RejectIssue(RejectIssueRequest) returns (RejectIssueResponse);
114+}
115+
116+// CreateIssueRequest describes the issue to open.
117+message CreateIssueRequest {
118+ // Repo is the repository name.
119+ string repo = 1;
120+ // Title is the one-line title. Required.
121+ string title = 2;
122+ // Body is the optional content.
123+ string body = 3;
124+}
125+
126+// CreateIssueResponse holds the new issue.
127+message CreateIssueResponse {
128+ // Issue is the created issue.
129+ Issue issue = 1;
130+}
131+
132+// ListIssuesRequest selects a page of issues.
133+message ListIssuesRequest {
134+ // Repo is the repository name.
135+ string repo = 1;
136+ // State restricts the result to one state. ISSUE_STATE_UNSPECIFIED means
137+ // every state.
138+ IssueState state = 2;
139+ // PageSize is the maximum number of issues to return. 0 means the server
140+ // default; values above the server maximum are lowered to it.
141+ int32 page_size = 3;
142+ // PageToken is the next_page_token of a previous response, to continue the
143+ // same listing. Empty for the first page. Other fields must be unchanged.
144+ string page_token = 4;
145+}
146+
147+// ListIssuesResponse is one page of issues.
148+message ListIssuesResponse {
149+ // Issues are the issues of this page, highest number first.
150+ repeated Issue issues = 1;
151+ // NextPageToken is passed as page_token to get the next page. Empty when
152+ // there is no next page.
153+ string next_page_token = 2;
154+}
155+
156+// GetIssueRequest selects an issue.
157+message GetIssueRequest {
158+ // Repo is the repository name.
159+ string repo = 1;
160+ // Number is the issue number.
161+ int64 number = 2;
162+}
163+
164+// GetIssueResponse holds an issue with its comments.
165+message GetIssueResponse {
166+ // Issue is the issue.
167+ Issue issue = 1;
168+ // Comments are every comment of the issue, oldest first.
169+ repeated IssueComment comments = 2;
170+}
171+
172+// AddIssueCommentRequest describes the comment to add.
173+message AddIssueCommentRequest {
174+ // Repo is the repository name.
175+ string repo = 1;
176+ // Number is the issue number.
177+ int64 number = 2;
178+ // Text is the content. Required.
179+ string text = 3;
180+}
181+
182+// AddIssueCommentResponse holds the new comment.
183+message AddIssueCommentResponse {
184+ // Comment is the created comment.
185+ IssueComment comment = 1;
186+}
187+
188+// CloseIssueRequest names the issue to close.
189+message CloseIssueRequest {
190+ // Repo is the repository name.
191+ string repo = 1;
192+ // Number is the issue number.
193+ int64 number = 2;
194+}
195+
196+// CloseIssueResponse holds the issue after closing.
197+message CloseIssueResponse {
198+ // Issue is the closed issue.
199+ Issue issue = 1;
200+}
201+
202+// ReopenIssueRequest names the issue to reopen.
203+message ReopenIssueRequest {
204+ // Repo is the repository name.
205+ string repo = 1;
206+ // Number is the issue number.
207+ int64 number = 2;
208+}
209+
210+// ReopenIssueResponse holds the issue after reopening.
211+message ReopenIssueResponse {
212+ // Issue is the reopened issue.
213+ Issue issue = 1;
214+}
215+
216+// PendingIssue is an issue awaiting moderation. It has no number yet.
217+message PendingIssue {
218+ // Id identifies the pending issue. Opaque internal id, not a number.
219+ string id = 1;
220+ // Repo is the name of the repository.
221+ string repo = 2;
222+ // Title is the one-line title.
223+ string title = 3;
224+ // Body is the content. It may be empty.
225+ string body = 4;
226+ // Author is the optional display name given by the anonymous submitter. It
227+ // may be empty or "anonymous", and is not verified: it never proves
228+ // identity (a visitor can type "owner"). It becomes Issue.author on approval.
229+ string author = 5;
230+ // CreatedAt is when the issue was submitted.
231+ google.protobuf.Timestamp created_at = 6;
232+}
233+
234+// ListPendingIssuesRequest selects a page of pending issues.
235+message ListPendingIssuesRequest {
236+ // Repo is the repository name. Empty means every repository.
237+ string repo = 1;
238+ // PageSize is the maximum number of issues to return. 0 means the server
239+ // default; values above the server maximum are lowered to it.
240+ int32 page_size = 2;
241+ // PageToken is the next_page_token of a previous response, to continue the
242+ // same listing. Empty for the first page. Other fields must be unchanged.
243+ string page_token = 3;
244+}
245+
246+// ListPendingIssuesResponse is one page of pending issues.
247+message ListPendingIssuesResponse {
248+ // Issues are the pending issues of this page, oldest first.
249+ repeated PendingIssue issues = 1;
250+ // NextPageToken is passed as page_token to get the next page. Empty when
251+ // there is no next page.
252+ string next_page_token = 2;
253+}
254+
255+// ApproveIssueRequest names the pending issue to approve.
256+message ApproveIssueRequest {
257+ // Repo is the repository name.
258+ string repo = 1;
259+ // Id is the PendingIssue.id.
260+ string id = 2;
261+}
262+
263+// ApproveIssueResponse holds the approved issue.
264+message ApproveIssueResponse {
265+ // Issue is the approved issue, with its new number and state
266+ // ISSUE_STATE_OPEN.
267+ Issue issue = 1;
268+}
269+
270+// RejectIssueRequest names the pending issue to reject.
271+message RejectIssueRequest {
272+ // Repo is the repository name.
273+ string repo = 1;
274+ // Id is the PendingIssue.id.
275+ string id = 2;
276+ // Ban also bans the submitter for a while.
277+ bool ban = 3;
278+}
279+
280+// RejectIssueResponse is empty: the pending issue is gone.
281+message RejectIssueResponse {}
@@ -0,0 +1,281 @@
1+// SPDX-License-Identifier: MIT
2+
3+syntax = "proto3";
4+
5+package ermite;
6+
7+import "google/protobuf/timestamp.proto";
8+
9+// IssueState is where an issue is in its life.
10+enum IssueState {
11+ // Not set. In ListIssuesRequest.state it means every state. Never returned
12+ // by the server.
13+ ISSUE_STATE_UNSPECIFIED = 0;
14+ // The issue is open.
15+ ISSUE_STATE_OPEN = 1;
16+ // The issue is closed.
17+ ISSUE_STATE_CLOSED = 2;
18+ // Reserved for later use: awaiting moderation, owner-only.
19+ ISSUE_STATE_PENDING = 3;
20+}
21+
22+// Issue is a discussion in a repository.
23+message Issue {
24+ // Repo is the name of the repository.
25+ string repo = 1;
26+ // Number identifies the issue within its repository.
27+ int64 number = 2;
28+ // Title is the one-line title.
29+ string title = 3;
30+ // Body is the content of the issue. It may be empty.
31+ string body = 4;
32+ // State is where the issue is in its life.
33+ IssueState state = 5;
34+ // Author names who created the issue: the label of the token or session
35+ // that was used ("root" for the root token, "owner" for a web session), or,
36+ // for an issue approved from the public form, the unverified display name
37+ // the visitor typed (or "anonymous"). It is informational and never proves
38+ // identity: labels are not unique, the token may be gone, and a visitor can
39+ // type "owner".
40+ string author = 6;
41+ // CreatedAt is when the issue was created.
42+ google.protobuf.Timestamp created_at = 7;
43+ // UpdatedAt is when the issue last changed.
44+ google.protobuf.Timestamp updated_at = 8;
45+ // CommentCount is the number of comments on the issue.
46+ int32 comment_count = 9;
47+}
48+
49+// IssueComment is a note on an issue.
50+message IssueComment {
51+ // Id identifies the comment within its issue. Opaque.
52+ string id = 1;
53+ // Author names who wrote the comment, like Issue.author.
54+ string author = 2;
55+ // Text is the content of the comment.
56+ string text = 3;
57+ // CreatedAt is when the comment was written.
58+ google.protobuf.Timestamp created_at = 4;
59+}
60+
61+// IssueService manages issues and their comments.
62+//
63+// Servers that implement this service list "issues" in
64+// GetVersionResponse.features. Issues and merge requests of a repository share
65+// one number sequence. When the issues mode of the repository is off, every
66+// call answers not_found, as for a repository that does not exist.
67+//
68+// Reading (ListIssues, GetIssue) needs the same access as reading the
69+// repository: nothing for a public one, the repo:read scope for a private one.
70+// Writing (CreateIssue, AddIssueComment, CloseIssue, ReopenIssue) needs the
71+// owner or a token with the issue:write scope on the repository; branch
72+// prefixes do not restrict it. A caller without credentials gets
73+// unauthenticated, one with an insufficient scope gets permission_denied, and
74+// one that cannot read the repository gets not_found.
75+//
76+// Text fields are normalized to NFC and stripped of control and Unicode
77+// bidirectional characters (line breaks and tabs stay in bodies and comments).
78+// The title is one line of 1 to 120 code points; body and comment text are at
79+// most 10,000 code points and a comment must not be blank. A text that breaks
80+// a rule is rejected with invalid_argument. A comment on an issue that already
81+// holds 1,000 comments gives resource_exhausted. Closing an issue that is not
82+// open, or reopening one that is not closed, gives failed_precondition.
83+//
84+// The ordinary RPCs only return issues in state OPEN or CLOSED, and
85+// ListIssues with state ISSUE_STATE_PENDING is invalid_argument. Submissions
86+// from the public form wait as PendingIssue records, reachable only through
87+// the three moderation RPCs (ListPendingIssues, ApproveIssue, RejectIssue).
88+// These need owner rights (root, an owner session, or a token holding every
89+// scope with no repository or branch limit); anyone else gets not_found,
90+// whatever the request holds. For the owner, a malformed pending issue id is
91+// not_found and a malformed repository name is invalid_argument.
92+service IssueService {
93+ // CreateIssue opens an issue. Needs the issue:write scope.
94+ rpc CreateIssue(CreateIssueRequest) returns (CreateIssueResponse);
95+ // ListIssues lists issues, highest number first, optionally in one state.
96+ rpc ListIssues(ListIssuesRequest) returns (ListIssuesResponse);
97+ // GetIssue returns an issue with its comments.
98+ rpc GetIssue(GetIssueRequest) returns (GetIssueResponse);
99+ // AddIssueComment adds a comment. Needs the issue:write scope.
100+ rpc AddIssueComment(AddIssueCommentRequest) returns (AddIssueCommentResponse);
101+ // CloseIssue closes an issue. Needs the issue:write scope.
102+ rpc CloseIssue(CloseIssueRequest) returns (CloseIssueResponse);
103+ // ReopenIssue reopens an issue. Needs the issue:write scope.
104+ rpc ReopenIssue(ReopenIssueRequest) returns (ReopenIssueResponse);
105+ // ListPendingIssues lists issues awaiting moderation, oldest first. Owner
106+ // rights only; anyone else gets NOT_FOUND.
107+ rpc ListPendingIssues(ListPendingIssuesRequest) returns (ListPendingIssuesResponse);
108+ // ApproveIssue turns a pending issue into an open issue with a new number.
109+ // Owner rights only; anyone else gets NOT_FOUND.
110+ rpc ApproveIssue(ApproveIssueRequest) returns (ApproveIssueResponse);
111+ // RejectIssue discards a pending issue, optionally banning its submitter.
112+ // Owner rights only; anyone else gets NOT_FOUND.
113+ rpc RejectIssue(RejectIssueRequest) returns (RejectIssueResponse);
114+}
115+
116+// CreateIssueRequest describes the issue to open.
117+message CreateIssueRequest {
118+ // Repo is the repository name.
119+ string repo = 1;
120+ // Title is the one-line title. Required.
121+ string title = 2;
122+ // Body is the optional content.
123+ string body = 3;
124+}
125+
126+// CreateIssueResponse holds the new issue.
127+message CreateIssueResponse {
128+ // Issue is the created issue.
129+ Issue issue = 1;
130+}
131+
132+// ListIssuesRequest selects a page of issues.
133+message ListIssuesRequest {
134+ // Repo is the repository name.
135+ string repo = 1;
136+ // State restricts the result to one state. ISSUE_STATE_UNSPECIFIED means
137+ // every state.
138+ IssueState state = 2;
139+ // PageSize is the maximum number of issues to return. 0 means the server
140+ // default; values above the server maximum are lowered to it.
141+ int32 page_size = 3;
142+ // PageToken is the next_page_token of a previous response, to continue the
143+ // same listing. Empty for the first page. Other fields must be unchanged.
144+ string page_token = 4;
145+}
146+
147+// ListIssuesResponse is one page of issues.
148+message ListIssuesResponse {
149+ // Issues are the issues of this page, highest number first.
150+ repeated Issue issues = 1;
151+ // NextPageToken is passed as page_token to get the next page. Empty when
152+ // there is no next page.
153+ string next_page_token = 2;
154+}
155+
156+// GetIssueRequest selects an issue.
157+message GetIssueRequest {
158+ // Repo is the repository name.
159+ string repo = 1;
160+ // Number is the issue number.
161+ int64 number = 2;
162+}
163+
164+// GetIssueResponse holds an issue with its comments.
165+message GetIssueResponse {
166+ // Issue is the issue.
167+ Issue issue = 1;
168+ // Comments are every comment of the issue, oldest first.
169+ repeated IssueComment comments = 2;
170+}
171+
172+// AddIssueCommentRequest describes the comment to add.
173+message AddIssueCommentRequest {
174+ // Repo is the repository name.
175+ string repo = 1;
176+ // Number is the issue number.
177+ int64 number = 2;
178+ // Text is the content. Required.
179+ string text = 3;
180+}
181+
182+// AddIssueCommentResponse holds the new comment.
183+message AddIssueCommentResponse {
184+ // Comment is the created comment.
185+ IssueComment comment = 1;
186+}
187+
188+// CloseIssueRequest names the issue to close.
189+message CloseIssueRequest {
190+ // Repo is the repository name.
191+ string repo = 1;
192+ // Number is the issue number.
193+ int64 number = 2;
194+}
195+
196+// CloseIssueResponse holds the issue after closing.
197+message CloseIssueResponse {
198+ // Issue is the closed issue.
199+ Issue issue = 1;
200+}
201+
202+// ReopenIssueRequest names the issue to reopen.
203+message ReopenIssueRequest {
204+ // Repo is the repository name.
205+ string repo = 1;
206+ // Number is the issue number.
207+ int64 number = 2;
208+}
209+
210+// ReopenIssueResponse holds the issue after reopening.
211+message ReopenIssueResponse {
212+ // Issue is the reopened issue.
213+ Issue issue = 1;
214+}
215+
216+// PendingIssue is an issue awaiting moderation. It has no number yet.
217+message PendingIssue {
218+ // Id identifies the pending issue. Opaque internal id, not a number.
219+ string id = 1;
220+ // Repo is the name of the repository.
221+ string repo = 2;
222+ // Title is the one-line title.
223+ string title = 3;
224+ // Body is the content. It may be empty.
225+ string body = 4;
226+ // Author is the optional display name given by the anonymous submitter. It
227+ // may be empty or "anonymous", and is not verified: it never proves
228+ // identity (a visitor can type "owner"). It becomes Issue.author on approval.
229+ string author = 5;
230+ // CreatedAt is when the issue was submitted.
231+ google.protobuf.Timestamp created_at = 6;
232+}
233+
234+// ListPendingIssuesRequest selects a page of pending issues.
235+message ListPendingIssuesRequest {
236+ // Repo is the repository name. Empty means every repository.
237+ string repo = 1;
238+ // PageSize is the maximum number of issues to return. 0 means the server
239+ // default; values above the server maximum are lowered to it.
240+ int32 page_size = 2;
241+ // PageToken is the next_page_token of a previous response, to continue the
242+ // same listing. Empty for the first page. Other fields must be unchanged.
243+ string page_token = 3;
244+}
245+
246+// ListPendingIssuesResponse is one page of pending issues.
247+message ListPendingIssuesResponse {
248+ // Issues are the pending issues of this page, oldest first.
249+ repeated PendingIssue issues = 1;
250+ // NextPageToken is passed as page_token to get the next page. Empty when
251+ // there is no next page.
252+ string next_page_token = 2;
253+}
254+
255+// ApproveIssueRequest names the pending issue to approve.
256+message ApproveIssueRequest {
257+ // Repo is the repository name.
258+ string repo = 1;
259+ // Id is the PendingIssue.id.
260+ string id = 2;
261+}
262+
263+// ApproveIssueResponse holds the approved issue.
264+message ApproveIssueResponse {
265+ // Issue is the approved issue, with its new number and state
266+ // ISSUE_STATE_OPEN.
267+ Issue issue = 1;
268+}
269+
270+// RejectIssueRequest names the pending issue to reject.
271+message RejectIssueRequest {
272+ // Repo is the repository name.
273+ string repo = 1;
274+ // Id is the PendingIssue.id.
275+ string id = 2;
276+ // Ban also bans the submitter for a while.
277+ bool ban = 3;
278+}
279+
280+// RejectIssueResponse is empty: the pending issue is gone.
281+message RejectIssueResponse {}
M proto/ermite/repo.proto +26 -4
@@ -20,8 +20,20 @@ enum Visibility {
2020 VISIBILITY_PRIVATE = 2;
2121 }
2222
23+// IssuesMode says who may write issues in a repository.
24+enum IssuesMode {
25+ // Not set.
26+ ISSUES_MODE_UNSPECIFIED = 0;
27+ // Issues are disabled.
28+ ISSUES_MODE_OFF = 1;
29+ // Only the owner may write issues.
30+ ISSUES_MODE_OWNER = 2;
31+ // Issues are open to public participation.
32+ ISSUES_MODE_PUBLIC = 3;
33+}
34+
2335 // RepoSettings are the settings of a repository that govern its merge
24-// requests.
36+// requests and issues.
2537 message RepoSettings {
2638 // AllowedMergeModes are the modes with which a merge request of the
2739 // repository may be merged. It is never empty in a response and has no
@@ -33,6 +45,8 @@ message RepoSettings {
3345 // explicitly, so a later change of the server default never alters an
3446 // existing repository.
3547 bool failing_checks_block = 2;
48+ // Issues is the issue participation mode of the repository.
49+ IssuesMode issues = 3;
3650 }
3751
3852 // Repo is the metadata of a repository. Git-owned state (refs, trees, history)
@@ -53,7 +67,7 @@ message Repo {
5367 google.protobuf.Timestamp created_at = 5;
5468 // PushedAt is the time of the last push. It is unset until the first push.
5569 google.protobuf.Timestamp pushed_at = 6;
56- // Settings are the merge settings of the repository. Always set in a
70+ // Settings are the settings of the repository. Always set in a
5771 // response, with the defaults for a repository that never changed them.
5872 RepoSettings settings = 7;
5973 // Topics classify the repository, as on GitHub: at most 20, each 1-50
@@ -153,8 +167,10 @@ message UpdateRepoRequest {
153167 string default_branch = 4;
154168 // UpdateMask lists the fields to change: "description", "visibility",
155169 // "default_branch", "topics", "settings.allowed_merge_modes" and/or
156- // "settings.failing_checks_block". "settings" stands for both settings
157- // paths. It must not be empty. Other paths are invalid_argument.
170+ // "settings.failing_checks_block". "settings" stands for both merge settings
171+ // paths. It must not be empty unless issues is set. Other paths are
172+ // invalid_argument, including "settings.issues" and "issues": the issues
173+ // field is applied outside the mask.
158174 google.protobuf.FieldMask update_mask = 5;
159175 // Settings holds the new merge settings. Only the fields named in
160176 // update_mask are used. allowed_merge_modes must then not be empty nor
@@ -165,6 +181,12 @@ message UpdateRepoRequest {
165181 // Topics replace the topics of the repository (see Repo.topics). Used only
166182 // if update_mask contains "topics"; an empty list then clears them.
167183 repeated string topics = 7;
184+ // Issues is the new issue participation mode. When absent it is unchanged.
185+ // It is applied whatever update_mask contains, and may be the only change
186+ // (an empty update_mask is then valid). When present it must not be
187+ // ISSUES_MODE_UNSPECIFIED: that is invalid_argument. Changing it needs the
188+ // repo:admin scope and a token with no branch limit, like any other field.
189+ optional IssuesMode issues = 8;
168190 }
169191
170192 // UpdateRepoResponse holds the repository after the update.
@@ -20,8 +20,20 @@ enum Visibility {
20 VISIBILITY_PRIVATE = 2;20 VISIBILITY_PRIVATE = 2;
21 }21 }
22 22
23+// IssuesMode says who may write issues in a repository.
24+enum IssuesMode {
25+ // Not set.
26+ ISSUES_MODE_UNSPECIFIED = 0;
27+ // Issues are disabled.
28+ ISSUES_MODE_OFF = 1;
29+ // Only the owner may write issues.
30+ ISSUES_MODE_OWNER = 2;
31+ // Issues are open to public participation.
32+ ISSUES_MODE_PUBLIC = 3;
33+}
34+
23 // RepoSettings are the settings of a repository that govern its merge35 // RepoSettings are the settings of a repository that govern its merge
24-// requests.36+// requests and issues.
25 message RepoSettings {37 message RepoSettings {
26 // AllowedMergeModes are the modes with which a merge request of the38 // AllowedMergeModes are the modes with which a merge request of the
27 // repository may be merged. It is never empty in a response and has no39 // repository may be merged. It is never empty in a response and has no
@@ -33,6 +45,8 @@ message RepoSettings {
33 // explicitly, so a later change of the server default never alters an45 // explicitly, so a later change of the server default never alters an
34 // existing repository.46 // existing repository.
35 bool failing_checks_block = 2;47 bool failing_checks_block = 2;
48+ // Issues is the issue participation mode of the repository.
49+ IssuesMode issues = 3;
36 }50 }
37 51
38 // Repo is the metadata of a repository. Git-owned state (refs, trees, history)52 // Repo is the metadata of a repository. Git-owned state (refs, trees, history)
@@ -53,7 +67,7 @@ message Repo {
53 google.protobuf.Timestamp created_at = 5;67 google.protobuf.Timestamp created_at = 5;
54 // PushedAt is the time of the last push. It is unset until the first push.68 // PushedAt is the time of the last push. It is unset until the first push.
55 google.protobuf.Timestamp pushed_at = 6;69 google.protobuf.Timestamp pushed_at = 6;
56- // Settings are the merge settings of the repository. Always set in a70+ // Settings are the settings of the repository. Always set in a
57 // response, with the defaults for a repository that never changed them.71 // response, with the defaults for a repository that never changed them.
58 RepoSettings settings = 7;72 RepoSettings settings = 7;
59 // Topics classify the repository, as on GitHub: at most 20, each 1-5073 // Topics classify the repository, as on GitHub: at most 20, each 1-50
@@ -153,8 +167,10 @@ message UpdateRepoRequest {
153 string default_branch = 4;167 string default_branch = 4;
154 // UpdateMask lists the fields to change: "description", "visibility",168 // UpdateMask lists the fields to change: "description", "visibility",
155 // "default_branch", "topics", "settings.allowed_merge_modes" and/or169 // "default_branch", "topics", "settings.allowed_merge_modes" and/or
156- // "settings.failing_checks_block". "settings" stands for both settings170+ // "settings.failing_checks_block". "settings" stands for both merge settings
157- // paths. It must not be empty. Other paths are invalid_argument.171+ // paths. It must not be empty unless issues is set. Other paths are
172+ // invalid_argument, including "settings.issues" and "issues": the issues
173+ // field is applied outside the mask.
158 google.protobuf.FieldMask update_mask = 5;174 google.protobuf.FieldMask update_mask = 5;
159 // Settings holds the new merge settings. Only the fields named in175 // Settings holds the new merge settings. Only the fields named in
160 // update_mask are used. allowed_merge_modes must then not be empty nor176 // update_mask are used. allowed_merge_modes must then not be empty nor
@@ -165,6 +181,12 @@ message UpdateRepoRequest {
165 // Topics replace the topics of the repository (see Repo.topics). Used only181 // Topics replace the topics of the repository (see Repo.topics). Used only
166 // if update_mask contains "topics"; an empty list then clears them.182 // if update_mask contains "topics"; an empty list then clears them.
167 repeated string topics = 7;183 repeated string topics = 7;
184+ // Issues is the new issue participation mode. When absent it is unchanged.
185+ // It is applied whatever update_mask contains, and may be the only change
186+ // (an empty update_mask is then valid). When present it must not be
187+ // ISSUES_MODE_UNSPECIFIED: that is invalid_argument. Changing it needs the
188+ // repo:admin scope and a token with no branch limit, like any other field.
189+ optional IssuesMode issues = 8;
168 }190 }
169 191
170 // UpdateRepoResponse holds the repository after the update.192 // UpdateRepoResponse holds the repository after the update.
M proto/ermite/token.proto +3 -0
@@ -42,6 +42,9 @@ enum Scope {
4242 // SCOPE_MR_REVIEW allows setting a verdict (approve, changes requested) on
4343 // merge request notes. Wire name: mr:review.
4444 SCOPE_MR_REVIEW = 10;
45+ // SCOPE_ISSUE_WRITE allows creating, commenting on, closing and reopening
46+ // issues. Wire name: issue:write.
47+ SCOPE_ISSUE_WRITE = 11;
4548 }
4649
4750 // Token describes a stored token. It never contains the secret nor its hash.
@@ -42,6 +42,9 @@ enum Scope {
42 // SCOPE_MR_REVIEW allows setting a verdict (approve, changes requested) on42 // SCOPE_MR_REVIEW allows setting a verdict (approve, changes requested) on
43 // merge request notes. Wire name: mr:review.43 // merge request notes. Wire name: mr:review.
44 SCOPE_MR_REVIEW = 10;44 SCOPE_MR_REVIEW = 10;
45+ // SCOPE_ISSUE_WRITE allows creating, commenting on, closing and reopening
46+ // issues. Wire name: issue:write.
47+ SCOPE_ISSUE_WRITE = 11;
45 }48 }
46 49
47 // Token describes a stored token. It never contains the secret nor its hash.50 // Token describes a stored token. It never contains the secret nor its hash.

Comments0 open / 0

no comments yet.

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
l
merge request: the list of files (j k move, enter opens, esc back)
M
merge request: the merge menu; there 1–3 pick the mode, D deletes the branch, enter merges
R
merge request: mark the head read
esc
close a dialog, leave a field

Branches 2

Tags 18

↑↓ moveenter openesc close