@@ -127,9 +127,9 @@ private static function maybeCreatePaginated(ServiceDetails $svc, MethodDescript
127127 $ catalog = $ svc ->catalog ;
128128 $ inputMsg = $ catalog ->msgsByFullname [$ desc ->getInputType ()];
129129 $ outputMsg = $ catalog ->msgsByFullname [$ desc ->getOutputType ()];
130- $ isRestOnly = $ svc ->transportType === Transport::REST ;
130+ $ isDireGapic = $ svc ->transportType === Transport::REST ;
131131 $ pageSize = $ inputMsg ->desc ->getFieldByName ('page_size ' );
132- if ($ isRestOnly && is_null ($ pageSize )) {
132+ if ($ isDireGapic && is_null ($ pageSize )) {
133133 $ pageSize = $ inputMsg ->desc ->getFieldByName ('max_results ' );
134134 }
135135
@@ -141,7 +141,7 @@ private static function maybeCreatePaginated(ServiceDetails $svc, MethodDescript
141141
142142 // If we have the type inside the ExplicitPagination class,
143143 // use the field stored in the paginations array
144- if (ExplicitPagination::exists ($ outputMsg ->desc ->getFullName ())) {
144+ if ($ isDireGapic && ExplicitPagination::exists ($ outputMsg ->desc ->getFullName ())) {
145145 $ resources = $ outputMsg ->desc ->getFieldByName (
146146 ExplicitPagination::getPagination ($ outputMsg ->desc ->getFullName ())
147147 );
@@ -152,20 +152,8 @@ private static function maybeCreatePaginated(ServiceDetails $svc, MethodDescript
152152 ->filter (fn ($ x ) => $ x [1 ]->isRepeated ());
153153 $ resourceByNumber = $ resourceCandidates ->orderBy (fn ($ x ) => $ x [0 ])->firstOrNull ();
154154
155- if ($ isRestOnly ) {
156- $ resourceListCandidates = $ resourceCandidates ->filter (fn ($ x ) => !ProtoHelpers::isMap ($ catalog , $ x [1 ]));
157- $ resourceMapCandidates = $ resourceCandidates ->filter (fn ($ x ) => ProtoHelpers::isMap ($ catalog , $ x [1 ]));
158-
159- // If there are more than one of either, do not generate a paginated method.
160- if (count ($ resourceListCandidates ) > 1 || count ($ resourceMapCandidates ) > 1 ) {
161- return null ;
162- }
163-
164- // A map field takes precedence over a repeated (i.e. list) field.
165- $ resourceByNumber = $ resourceMapCandidates ->orderBy (fn ($ x ) => $ x [0 ])->firstOrNull ();
166- if (is_null ($ resourceByNumber )) {
167- $ resourceByNumber = $ resourceListCandidates ->orderBy (fn ($ x ) => $ x [0 ])->firstOrNull ();
168- }
155+ if ($ isDireGapic ) {
156+ $ resourceByNumber = self ::getCandidate ($ resourceCandidates , $ catalog );
169157 }
170158
171159 $ resourceByPosition = $ resourceCandidates ->firstOrNull ();
@@ -176,7 +164,7 @@ private static function maybeCreatePaginated(ServiceDetails $svc, MethodDescript
176164 $ resourceFieldValid = !is_null ($ resources );
177165
178166 // Leverage short-circuting.
179- if ($ resourceFieldValid && !$ isRestOnly ) {
167+ if ($ resourceFieldValid && !$ isDireGapic ) {
180168 $ resourceFieldValid &= !ProtoHelpers::isMap ($ catalog , $ resources )
181169 && $ resourceByNumber [0 ] === $ resourceByPosition [0 ];
182170 }
@@ -186,13 +174,13 @@ private static function maybeCreatePaginated(ServiceDetails $svc, MethodDescript
186174 }
187175
188176 $ isValidPageSize = !$ pageSize ->isRepeated ();
189- if ($ isRestOnly ) {
177+ if ($ isDireGapic ) {
190178 $ isValidPageSize = $ pageSize ->getType () === GPBType::UINT32 || $ pageSize ->getType () === GPBType::INT32 ;
191179 } else {
192180 $ isValidPageSize = $ pageSize ->getType () === GPBType::INT32 ;
193181 }
194182 if (!$ isValidPageSize ) {
195- throw new \Exception ("page_size field must be of type " . ($ isRestOnly ? "uint32 or int32 " : "int32 " ) . ". " );
183+ throw new \Exception ("page_size field must be of type " . ($ isDireGapic ? "uint32 or int32 " : "int32 " ) . ". " );
196184 }
197185 if ($ pageToken ->isRepeated () || $ pageToken ->getType () !== GPBType::STRING ) {
198186 throw new \Exception ("page_token field must be of type string. " );
@@ -201,7 +189,7 @@ private static function maybeCreatePaginated(ServiceDetails $svc, MethodDescript
201189 throw new \Exception ("next_page_token field must be of type string. " );
202190 }
203191 if (!$ resourceFieldValid ) {
204- if ($ isRestOnly ) {
192+ if ($ isDireGapic ) {
205193 throw new \Exception ("Item resources field must a map or repeated field. " );
206194 }
207195 throw new \Exception ("Item resources field must be the first repeated field by number and position. " );
@@ -448,6 +436,64 @@ public function __construct($svc, $desc)
448436 };
449437 }
450438
439+ /**
440+ * Determine if the method should paginated for DIREGAPICs, which do not have annotations for pagination
441+ * yet. The following heuristic is used for selecting the field for pagination:
442+ * 1. If exactly one map field exists, select that field
443+ * 2. If exactly one list field exists, select that field
444+ * 3. If more than one list field exists, but exactly one of those fields is a message, select that field
445+ * 4. Otherwise, do not paginate
446+ */
447+ private static function getCandidate (Vector $ resourceCandidates , ProtoCatalog $ catalog ): null |array
448+ {
449+ $ resourceListCandidates = $ resourceCandidates ->filter (fn ($ x ) => !ProtoHelpers::isMap ($ catalog , $ x [1 ]));
450+ $ resourceMapCandidates = $ resourceCandidates ->filter (fn ($ x ) => ProtoHelpers::isMap ($ catalog , $ x [1 ]));
451+
452+ // If only one map exists, return it.
453+ if (count ($ resourceMapCandidates ) === 1 ) {
454+ return $ resourceMapCandidates ->firstOrNull ();
455+ }
456+
457+ // If only one list exists, return it.
458+ if (count ($ resourceListCandidates ) === 1 ) {
459+ return $ resourceListCandidates ->firstOrNull ();
460+ }
461+
462+ // If there are more than one repeated field,
463+ // search for a non primitive one.
464+ if (count ($ resourceListCandidates ) > 1 ) {
465+ $ resourceCandidateIndex = null ;
466+
467+ foreach ($ resourceListCandidates as $ index => $ candidate ) {
468+ $ descriptor = $ candidate [1 ];
469+
470+ // If is a primitive type, we cannot use it for pagination.
471+ // Search for another one.
472+ if ($ descriptor ->getType () !== GPBType::MESSAGE ) {
473+ continue ;
474+ }
475+
476+ // If we already find a non primitive type
477+ // then we have more than one.
478+ // unable to paginate.
479+ if (!is_null ($ resourceCandidateIndex )) {
480+ return null ;
481+ }
482+
483+ $ resourceCandidateIndex = $ index ;
484+ }
485+
486+ if (is_null ($ resourceCandidateIndex )) {
487+ return null ;
488+ }
489+
490+ return $ resourceListCandidates [$ resourceCandidateIndex ];
491+ }
492+
493+ // We cannot determine what is the pagination.
494+ return null ;
495+ }
496+
451497 /** @var ServiceDetails The service that contains this method. */
452498 public ServiceDetails $ serviceDetails ;
453499
0 commit comments