Skip to content

Commit 6f1df00

Browse files
committed
feat(sidekick/rust): Generate samples for list RPCs
List RPCs are understood as those defined by AIP 132 and AIP 4233
1 parent b04a39b commit 6f1df00

File tree

6 files changed

+367
-40
lines changed

6 files changed

+367
-40
lines changed

internal/sidekick/api/model.go

Lines changed: 87 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -390,11 +390,6 @@ func (m *Method) IsLRO() bool {
390390
return m.OperationInfo != nil
391391
}
392392

393-
// IsSimpleOrLRO returns true if the method is simple or a long-running operation.
394-
func (m *Method) IsSimpleOrLRO() bool {
395-
return m.IsSimple() || m.IsLRO()
396-
}
397-
398393
// LongRunningResponseType returns the response type of the long-running operation.
399394
func (m *Method) LongRunningResponseType() *Message {
400395
if m.OperationInfo == nil {
@@ -409,12 +404,23 @@ func (m *Method) LongRunningReturnsEmpty() bool {
409404
return responseType != nil && responseType.ID == ".google.protobuf.Empty"
410405
}
411406

407+
// IsList returns true if the method is a list operation.
408+
func (m *Method) IsList() bool {
409+
return m.OutputType != nil && m.OutputType.Pagination != nil
410+
}
411+
412+
// IsStreaming returns true if the method is client-side or server-side streaming.
413+
func (m *Method) IsStreaming() bool {
414+
return m.ClientSideStreaming || m.ServerSideStreaming
415+
}
416+
412417
// IsAIPStandard returns true if the method is one of the AIP standard methods.
413418
// IsAIPStandard simplifies writing mustache templates, mostly for samples.
414419
func (m *Method) IsAIPStandard() bool {
415420
return m.AIPStandardGetInfo() != nil ||
416421
m.AIPStandardDeleteInfo() != nil ||
417-
m.AIPStandardUndeleteInfo() != nil
422+
m.AIPStandardUndeleteInfo() != nil ||
423+
m.AIPStandardListInfo() != nil
418424
}
419425

420426
// AIPStandardGetInfo contains information relevant to get operations
@@ -537,11 +543,67 @@ func (m *Method) AIPStandardUndeleteInfo() *AIPStandardUndeleteInfo {
537543
}
538544
}
539545

546+
// AIPStandardListInfo contains information relevant to list operations
547+
// that are like those defined by AIP-132.
548+
type AIPStandardListInfo struct {
549+
// ParentRequestField is the field in the method input that contains the parent resource name
550+
// of the resources that the list operation should fetch.
551+
ParentRequestField *Field
552+
}
553+
554+
// AIPStandardListInfo returns information relevant to a list operation that is like
555+
// a list operation as defined by AIP-132, if the method is such an operation.
556+
func (m *Method) AIPStandardListInfo() *AIPStandardListInfo {
557+
if !m.IsList() || m.InputType == nil {
558+
return nil
559+
}
560+
561+
// Standard list methods for resource "Foo" should be named "ListFoos".
562+
maybePlural, found := strings.CutPrefix(strings.ToLower(m.Name), "list")
563+
if !found || maybePlural == "" {
564+
return nil
565+
}
566+
567+
// The request name should be "ListFoosRequest".
568+
if strings.ToLower(m.InputType.Name) != fmt.Sprintf("list%srequest", maybePlural) {
569+
return nil
570+
}
571+
572+
// The response name should be "ListFoosResponse".
573+
if strings.ToLower(m.OutputType.Name) != fmt.Sprintf("list%sresponse", maybePlural) {
574+
return nil
575+
}
576+
577+
// Identify the listed resource type.
578+
pageableItem := m.OutputType.Pagination.PageableItem
579+
if pageableItem == nil || pageableItem.MessageType == nil || pageableItem.MessageType.Resource == nil {
580+
// If we can't identify the resource, we can't match strictly.
581+
// However, standard AIP-132 implies we should be able to.
582+
return nil
583+
}
584+
resourceType := pageableItem.MessageType.Resource.Type
585+
586+
// The request needs to have a field for the parent.
587+
parentField := findBestParentFieldByType(m.InputType, resourceType)
588+
589+
if parentField == nil {
590+
return nil
591+
}
592+
593+
return &AIPStandardListInfo{
594+
ParentRequestField: parentField,
595+
}
596+
}
597+
540598
const (
541599
// StandardFieldNameForResourceRef is the standard name for resource references
542600
// to the resource being operated on by standard methods as defined by AIPs.
543601
StandardFieldNameForResourceRef = "name"
544602

603+
// StandardFieldNameForParentResourceRef is the standard name for resource references
604+
// to the child resource being operated on by standard methods as defined by AIPs.
605+
StandardFieldNameForParentResourceRef = "parent"
606+
545607
// GenericResourceType is a special resource type that may be used by resource references
546608
// in contexts where the referenced resource may be of any type, as defined by AIPs.
547609
GenericResourceType = "*"
@@ -614,6 +676,25 @@ func findBestResourceFieldBySingular(message *Message, resourcesByType map[strin
614676
return bestField
615677
}
616678

679+
// findBestParentFieldByType finds the best field in the message that references
680+
// the parent of a resource of the given type.
681+
//
682+
// We prioritize the matches as follows:
683+
// 1. The field name is "parent".
684+
// 2. The field references the child type.
685+
func findBestParentFieldByType(message *Message, childType string) *Field {
686+
var bestField *Field
687+
for _, field := range message.Fields {
688+
if field.Name == StandardFieldNameForParentResourceRef {
689+
return field
690+
}
691+
if field.ResourceReference != nil && field.ResourceReference.ChildType == childType {
692+
bestField = field
693+
}
694+
}
695+
return bestField
696+
}
697+
617698
// PathInfo contains normalized request path information.
618699
type PathInfo struct {
619700
// The list of bindings, including the top-level binding.

0 commit comments

Comments
 (0)