go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/analysis/internal/bugs/buganizer/client.go (about) 1 // Copyright 2022 The LUCI Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package buganizer 16 17 import ( 18 "context" 19 "fmt" 20 21 "github.com/google/s2a-go" 22 "google.golang.org/api/option" 23 "google.golang.org/api/option/internaloption" 24 "google.golang.org/grpc" 25 "google.golang.org/grpc/credentials/oauth" 26 27 "go.chromium.org/luci/common/errors" 28 "go.chromium.org/luci/third_party/google.golang.org/genproto/googleapis/devtools/issuetracker/v1" 29 issuetrackerclient "go.chromium.org/luci/third_party/google.golang.org/google/devtools/issuetracker/v1" 30 ) 31 32 const s2aServerAddr = "metadata.google.internal:80" 33 34 // RPCClient is an implementation of the client wrapper that uses the Client provided 35 // by issuetrackerclient package. This client acts as a delegate and 36 // a proxy to the actual implementation. 37 type RPCClient struct { 38 Client *issuetrackerclient.Client 39 } 40 41 // NewRPCClient returns a new ClientWrapper. 42 func NewRPCClient(ctx context.Context) (*RPCClient, error) { 43 buganizerEndpointBase := ctx.Value(&BuganizerEndpointBaseKey) 44 if buganizerEndpointBase == nil { 45 return nil, errors.New("Buganizer endpoint base is required for RPC client") 46 } 47 48 buganizerEndpointOAuthScope := ctx.Value(&BuganizerEndpointOAuthScopeKey) 49 if buganizerEndpointOAuthScope == nil { 50 return nil, errors.New("Buganizer OAuth scope is required for RPC client") 51 } 52 53 perRPCCreds, err := oauth.NewApplicationDefault(context.Background(), 54 "https://www.googleapis.com/auth/cloud-platform", 55 buganizerEndpointOAuthScope.(string), 56 ) 57 if err != nil { 58 return nil, err 59 } 60 61 clientOpts := &s2a.ClientOptions{ 62 S2AAddress: s2aServerAddr, 63 } 64 creds, err := s2a.NewClientCreds(clientOpts) 65 if err != nil { 66 return nil, err 67 } 68 69 client, err := issuetrackerclient.NewClient( 70 ctx, 71 internaloption.WithDefaultEndpoint(fmt.Sprintf("%v.googleapis.com:443", buganizerEndpointBase)), 72 internaloption.WithDefaultMTLSEndpoint(fmt.Sprintf("%v.mtls.googleapis.com:443", buganizerEndpointBase)), 73 internaloption.WithDefaultAudience(fmt.Sprintf("https://%v.googleapis.com/", buganizerEndpointBase)), 74 option.WithGRPCDialOption( 75 grpc.WithReturnConnectionError(), 76 ), 77 option.WithGRPCDialOption( 78 grpc.WithTransportCredentials(creds), 79 ), 80 option.WithGRPCDialOption( 81 grpc.WithPerRPCCredentials(perRPCCreds), 82 ), 83 option.WithEndpoint(fmt.Sprintf("%v.mtls.googleapis.com:443", buganizerEndpointBase)), 84 ) 85 86 if err != nil { 87 return nil, errors.Annotate(err, "create new wrapper client").Err() 88 } 89 return &RPCClient{ 90 Client: client, 91 }, nil 92 } 93 94 func (w *RPCClient) Close() { 95 w.Client.Close() 96 } 97 98 // BatchGetIssues delegates a call to Client.BatchGetIssues and 99 // returns the list of issues returned or the error that occured. 100 func (w *RPCClient) BatchGetIssues(ctx context.Context, in *issuetracker.BatchGetIssuesRequest) (*issuetracker.BatchGetIssuesResponse, error) { 101 return w.Client.BatchGetIssues(ctx, in) 102 } 103 104 // GetIssue delegates a call to Client.GetIssue and returns the issue 105 // returned or the error that occured. 106 func (w *RPCClient) GetIssue(ctx context.Context, in *issuetracker.GetIssueRequest) (*issuetracker.Issue, error) { 107 return w.Client.GetIssue(ctx, in) 108 } 109 110 // CreateIssue delegates a call to Client.CreateIssue and returns the 111 // issue that was created or the error that occured. 112 func (w *RPCClient) CreateIssue(ctx context.Context, in *issuetracker.CreateIssueRequest) (*issuetracker.Issue, error) { 113 return w.Client.CreateIssue(ctx, in) 114 } 115 116 // ModifyIssue delegates a call to Client.ModifyIssue and returns the 117 // modified issue or the error that occured. 118 func (w *RPCClient) ModifyIssue(ctx context.Context, in *issuetracker.ModifyIssueRequest) (*issuetracker.Issue, error) { 119 return w.Client.ModifyIssue(ctx, in) 120 } 121 122 // ListIssueUpdates delegates a call to Client.ListIssueUpdates and returns the 123 // issue updates iterator delegate. 124 func (w *RPCClient) ListIssueUpdates(ctx context.Context, in *issuetracker.ListIssueUpdatesRequest) IssueUpdateIterator { 125 it := w.Client.ListIssueUpdates(ctx, in) 126 return it 127 } 128 129 // CreateIssueComment delegates a call to Client.CreateIssueComment and returns 130 // the comment that was created or the error that occured. 131 func (w *RPCClient) CreateIssueComment(ctx context.Context, in *issuetracker.CreateIssueCommentRequest) (*issuetracker.IssueComment, error) { 132 return w.Client.CreateIssueComment(ctx, in) 133 } 134 135 // UpdateIssueComment delegates a call to Client.UpdateIssueComment and returns 136 // the updated comment or the error that occured. 137 func (w *RPCClient) UpdateIssueComment(ctx context.Context, in *issuetracker.UpdateIssueCommentRequest) (*issuetracker.IssueComment, error) { 138 return w.Client.UpdateIssueComment(ctx, in) 139 } 140 141 // ListIssueComments delegates a call to Client.ListIssueComments and returns 142 // the issue comment iterator delegate. 143 func (w *RPCClient) ListIssueComments(ctx context.Context, in *issuetracker.ListIssueCommentsRequest) IssueCommentIterator { 144 it := w.Client.ListIssueComments(ctx, in) 145 return it 146 } 147 148 // GetAutomationAccess delegates a call to Client.GetIssue and returns the access 149 // returned or the error that occured. 150 func (w *RPCClient) GetAutomationAccess(ctx context.Context, in *issuetracker.GetAutomationAccessRequest) (*issuetracker.GetAutomationAccessResponse, error) { 151 return w.Client.GetAutomationAccess(ctx, in) 152 } 153 154 // CreateHotlistEntry delegates a call to Client.CreateHotlistEntry and returns the created 155 // hotlist entry or the error that occured. 156 func (w *RPCClient) CreateHotlistEntry(ctx context.Context, in *issuetracker.CreateHotlistEntryRequest) (*issuetracker.HotlistEntry, error) { 157 return w.Client.CreateHotlistEntry(ctx, in) 158 }