github.com/googleapis/api-linter@v1.65.2/rules/internal/utils/method.go (about) 1 // Copyright 2023 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 utils 16 17 import ( 18 "regexp" 19 20 "github.com/jhump/protoreflect/desc" 21 ) 22 23 var ( 24 createMethodRegexp = regexp.MustCompile("^Create(?:[A-Z]|$)") 25 getMethodRegexp = regexp.MustCompile("^Get(?:[A-Z]|$)") 26 listMethodRegexp = regexp.MustCompile("^List(?:[A-Z]|$)") 27 listRevisionsMethodRegexp = regexp.MustCompile(`^List(?:[A-Za-z0-9]+)Revisions$`) 28 updateMethodRegexp = regexp.MustCompile("^Update(?:[A-Z]|$)") 29 deleteMethodRegexp = regexp.MustCompile("^Delete(?:[A-Z]|$)") 30 deleteRevisionMethodRegexp = regexp.MustCompile("^Delete[A-Za-z0-9]*Revision$") 31 legacyListRevisionsURINameRegexp = regexp.MustCompile(`:listRevisions$`) 32 ) 33 34 // IsCreateMethod returns true if this is a AIP-133 Create method. 35 func IsCreateMethod(m *desc.MethodDescriptor) bool { 36 return createMethodRegexp.MatchString(m.GetName()) 37 } 38 39 // IsCreateMethodWithResolvedReturnType returns true if this is a AIP-133 Create method with 40 // a non-nil response type. This method should be used for filtering in linter 41 // rules which access the response type of the method, to avoid crashing due 42 // to dereferencing a nil pointer to the response. 43 func IsCreateMethodWithResolvedReturnType(m *desc.MethodDescriptor) bool { 44 if !IsCreateMethod(m) { 45 return false 46 } 47 48 return GetResponseType(m) != nil 49 } 50 51 // IsGetMethod returns true if this is a AIP-131 Get method. 52 func IsGetMethod(m *desc.MethodDescriptor) bool { 53 methodName := m.GetName() 54 if methodName == "GetIamPolicy" { 55 return false 56 } 57 return getMethodRegexp.MatchString(methodName) 58 } 59 60 // IsListMethod return true if this is an AIP-132 List method 61 func IsListMethod(m *desc.MethodDescriptor) bool { 62 return listMethodRegexp.MatchString(m.GetName()) && !IsLegacyListRevisionsMethod(m) 63 } 64 65 // IsLegacyListRevisions identifies such a method by having the appropriate 66 // method name, having a `name` field instead of parent, and a HTTP suffix of 67 // `listRevisions`. 68 func IsLegacyListRevisionsMethod(m *desc.MethodDescriptor) bool { 69 // Must be named like List{Resource}Revisions. 70 if !listRevisionsMethodRegexp.MatchString(m.GetName()) { 71 return false 72 } 73 74 // Must have a `name` field instead of `parent`. 75 if m.GetInputType().FindFieldByName("name") == nil { 76 return false 77 } 78 79 // Must have the `:listRevisions` HTTP URI suffix. 80 if !HasHTTPRules(m) { 81 // If it doesn't have HTTP bindings, we shouldn't proceed to the next 82 // check, but a List{Resource}Revisions method with a `name` field is 83 // probably enough to be sure in the absence of HTTP bindings. 84 return true 85 } 86 87 // Just check the first bidning as they should all have the same suffix. 88 h := GetHTTPRules(m)[0].GetPlainURI() 89 return legacyListRevisionsURINameRegexp.MatchString(h) 90 } 91 92 // IsUpdateMethod returns true if this is a AIP-134 Update method 93 func IsUpdateMethod(m *desc.MethodDescriptor) bool { 94 methodName := m.GetName() 95 return updateMethodRegexp.MatchString(methodName) 96 } 97 98 // Returns true if this is a AIP-135 Delete method, false otherwise. 99 func IsDeleteMethod(m *desc.MethodDescriptor) bool { 100 return deleteMethodRegexp.MatchString(m.GetName()) && !deleteRevisionMethodRegexp.MatchString(m.GetName()) 101 } 102 103 // GetListResourceMessage returns the resource for a list method, 104 // nil otherwise. 105 func GetListResourceMessage(m *desc.MethodDescriptor) *desc.MessageDescriptor { 106 repeated := GetRepeatedMessageFields(m.GetOutputType()) 107 if len(repeated) > 0 { 108 return repeated[0].GetMessageType() 109 } 110 return nil 111 } 112 113 // IsStreaming returns if the method is either client or server streaming. 114 func IsStreaming(m *desc.MethodDescriptor) bool { 115 return m.IsClientStreaming() || m.IsServerStreaming() 116 }