private CompletableFuture<Page<NamedBlobRecord>> listRecursivelyInternal(String accountName, String containerName,
        String blobNamePrefix, String pageToken, int maxKey, boolean groupDirectories,
        Page<NamedBlobRecord> aggregatedPage) {

      return namedBlobDb.list(accountName, containerName, blobNamePrefix, pageToken, maxKey)
          .thenCompose(currentPage -> {
            // Merge the current page into the aggregated page.
            Page<NamedBlobRecord> updatedAggregatedPage =
                mergePageResults(aggregatedPage, currentPage, accountName, containerName, blobNamePrefix, maxKey,
                    groupDirectories);
            String tokenToUse = updatedAggregatedPage.getNextPageToken();

            // If no token is available, no further pages exist. Return the updated aggregated page.
            // This is the final result
            if (tokenToUse == null) {
              return CompletableFuture.completedFuture(updatedAggregatedPage);
            }
            // If we haven't reached maxKey yet, continue fetching.
            if (updatedAggregatedPage.getEntries().size() < maxKey) {
              return listRecursivelyInternal(accountName, containerName, blobNamePrefix, tokenToUse, maxKey,
                  groupDirectories, updatedAggregatedPage);
            } else {
              // Aggregated page is full.
              NamedBlobRecord lastRecord =
                  updatedAggregatedPage.getEntries().get(updatedAggregatedPage.getEntries().size() - 1);
              // If the last record is a directory and tokenToUse starts with that directory's blobName,
              // then continue fetching so that we merge further entries from that directory.
              if (lastRecord.isDirectory() && tokenToUse.startsWith(lastRecord.getBlobName())) {
                return listRecursivelyInternal(accountName, containerName, blobNamePrefix, tokenToUse, maxKey,
                    groupDirectories, updatedAggregatedPage);
              } else {
                // Otherwise, return the updated aggregated page. This is the final result.
                return CompletableFuture.completedFuture(updatedAggregatedPage);
              }
            }
          });
    }
Internal recursive helper that aggregates pages returned by the underlying list() API into a single Page.
<p>
This method calls the underlying NamedBlobDb.list() API with the given pageToken and merges the returned Page
with the previously aggregated results (contained in {@code aggregatedPage}) using {@link this#mergePageResults}.
If the aggregated Page is not yet full (i.e. contains fewer than {@code maxKey} entries) or the last entry is
a directory whose blobName prefixes the next page token, the method recurses to merge further entries from that
directory; otherwise, it returns the aggregated Page.
</p>
@param accountName      the account name.
@param containerName    the container name.
@param blobNamePrefix   the blob name prefix.
@param pageToken        the token for the current page.
@param maxKey           the maximum number of entries to accumulate.
@param groupDirectories flag indicating whether to group directories.
@param aggregatedPage   the aggregated Page so far (immutable).
@return a CompletableFuture containing a new aggregated Page (immutable) with updated entries and nextPageToken.
ambry-frontend/src/main/java/com/github/ambry/frontend/NamedBlobListHandler.java