github.com/googleapis/api-linter@v1.65.2/rules/aip0135/response_message_name.go (about) 1 // Copyright 2019 Google LLC 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 // https://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 aip0135 16 17 import ( 18 "fmt" 19 "strings" 20 21 "bitbucket.org/creachadair/stringset" 22 "github.com/googleapis/api-linter/lint" 23 "github.com/googleapis/api-linter/locations" 24 "github.com/googleapis/api-linter/rules/internal/utils" 25 "github.com/jhump/protoreflect/desc" 26 ) 27 28 // Delete messages should use google.protobuf.Empty, 29 // google.longrunning.Operation, or the resource itself as the response 30 // message. 31 var responseMessageName = &lint.MethodRule{ 32 Name: lint.NewRuleName(135, "response-message-name"), 33 OnlyIf: utils.IsDeleteMethod, 34 LintMethod: func(m *desc.MethodDescriptor) []lint.Problem { 35 resource := strings.Replace(m.GetName(), "Delete", "", 1) 36 37 // Rule check: Establish that for methods such as `DeleteFoo`, the response 38 // message is `google.protobuf.Empty` or `Foo`. 39 got := m.GetOutputType().GetName() 40 if stringset.New("Empty", "Operation").Contains(got) { 41 got = m.GetOutputType().GetFullyQualifiedName() 42 } 43 want := stringset.New(resource, "google.protobuf.Empty") 44 45 // If the return type is an Operation, use the annotated response type. 46 lro := false 47 if utils.IsOperation(m.GetOutputType()) { 48 got = utils.GetOperationInfo(m).GetResponseType() 49 lro = true 50 } 51 52 // If we did not get a permitted value, return a problem. 53 // 54 // Note: If `got` is empty string, this is an unannotated LRO. 55 // The AIP-151 rule will whine about that, and this rule should not as it 56 // would be confusing. 57 if !want.Contains(got) && got != "" { 58 // Customize the error message (by including Empty iff the resource is 59 // not marked declarative-friendly) 60 msg := "Delete RPCs should have response message type of Empty or the resource, not %q." 61 suggestion := "google.protobuf.Empty" 62 63 // Customize the location based on whether an LRO is in use. 64 location := locations.MethodResponseType(m) 65 if lro { 66 location = locations.MethodOperationInfo(m) 67 suggestion = "" // We can not offer a precise enough location to make a suggestion. 68 } 69 70 // Create and return the problem. 71 problem := lint.Problem{ 72 Message: fmt.Sprintf(msg, got), 73 Descriptor: m, 74 Location: location, 75 } 76 if len(suggestion) > 0 { 77 problem.Suggestion = suggestion 78 } 79 return []lint.Problem{problem} 80 } 81 82 return nil 83 }, 84 }