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  }