Skip to content

Commit 9a45911

Browse files
authored
Merge pull request #217 from microsoftgraph/po/collectionPaging
Add support for paging of collection.
2 parents 6c4b62c + ebb811e commit 9a45911

File tree

3 files changed

+132
-4
lines changed

3 files changed

+132
-4
lines changed

src/Beta/Users.User/Users.User/readme.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,6 @@ input-file: $(spec-doc-repo)/$(title).yml
3434
### Versioning
3535
3636
``` yaml
37-
module-version: 0.5.1
37+
module-version: 0.7.0
3838
release-notes: See https://aka.ms/GraphPowerShell-Release.
3939
```

src/readme.graph.md

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,9 @@ directive:
8181
parameter-name: Top
8282
set:
8383
parameter-name: PageSize
84-
alias: Top
84+
alias:
85+
- Top
86+
- Limit
8587
- where:
8688
parameter-name: Select
8789
set:
@@ -378,7 +380,7 @@ directive:
378380
}
379381
return $;
380382
}
381-
# Temporarily disable paging.
383+
# Add custom -All parameter to *_List cmdlets that support Odata next link.
382384
- from: source-file-csharp
383385
where: $
384386
transform: >
@@ -388,7 +390,15 @@ directive:
388390
} else {
389391
let odataNextLinkRegex = /(^\s*)(if\s*\(\s*result.OdataNextLink\s*!=\s*null\s*\))/gmi
390392
if($.match(odataNextLinkRegex)) {
391-
$ = $.replace(odataNextLinkRegex, '$1result.OdataNextLink = null;\n$1if (result.OdataNextLink != null)$1');
393+
$ = $.replace(odataNextLinkRegex, '$1if (result.OdataNextLink != null && this.ShouldIteratePages(this.InvocationInformation.BoundParameters, result.Value.Length))\n$1');
394+
let psBaseClassImplementationRegex = /(\s*:\s*)(global::System.Management.Automation.PSCmdlet)/gmi
395+
$ = $.replace(psBaseClassImplementationRegex, '$1Microsoft.Graph.PowerShell.Cmdlets.Custom.ListCmdlet');
396+
397+
let beginProcessingRegex = /(^\s*)(protected\s*override\s*void\s*BeginProcessing\(\)\s*{)/gmi
398+
$ = $.replace(beginProcessingRegex, '$1$2\n$1$1if (this.InvocationInformation.BoundParameters.ContainsKey("PageSize")){ InitializePaging(ref this.__invocationInfo, ref this._pageSize); }\n$1');
399+
400+
let odataNextLinkCallRegex = /(^\s*)(await\s*this\.Client\.UsersUserListUser_Call\(requestMessage\,\s*onOk\,\s*onDefault\,\s*this\,\s*Pipeline\)\;)/gmi
401+
$ = $.replace(odataNextLinkCallRegex, '$1requestMessage.RequestUri = GetOverflowItemsNextLinkUri(requestMessage.RequestUri);\n$1$2');
392402
}
393403
return $;
394404
}

tools/Custom/ListCmdlet.cs

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
// ------------------------------------------------------------------------------
2+
// Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. See License in the project root for license information.
3+
// ------------------------------------------------------------------------------
4+
namespace Microsoft.Graph.PowerShell.Cmdlets.Custom
5+
{
6+
7+
public partial class ListCmdlet : global::System.Management.Automation.PSCmdlet
8+
{
9+
/// <summary>Backing field for <see cref="All" /> property.</summary>
10+
private global::System.Management.Automation.SwitchParameter _all;
11+
12+
/// <summary>List All pages</summary>
13+
[global::System.Management.Automation.Parameter(Mandatory = false, HelpMessage = "List all pages")]
14+
[Microsoft.Graph.PowerShell.Runtime.Info(
15+
Required = false,
16+
ReadOnly = false,
17+
Description = @"List all pages",
18+
SerializedName = @"$all",
19+
PossibleTypes = new[] { typeof(global::System.Management.Automation.SwitchParameter) })]
20+
[global::Microsoft.Graph.PowerShell.Category(global::Microsoft.Graph.PowerShell.ParameterCategory.Runtime)]
21+
public global::System.Management.Automation.SwitchParameter All { get => this._all; set => this._all = value; }
22+
23+
/// <summary>
24+
/// Default number of items per page.
25+
/// </summary>
26+
internal const int DefaultPageSize = 100;
27+
28+
/// <summary>
29+
/// Maximum number of items per page.
30+
/// </summary>
31+
internal const int MaxPageSize = 999;
32+
33+
/// <summary>
34+
/// Original page size/top/limit passed to Cmdlet via parameter.
35+
/// </summary>
36+
internal int originalPageSize = 0;
37+
38+
/// <summary>
39+
/// Total number of pages required to iterate page collections excluding overflow items.
40+
/// </summary>
41+
internal int requiredPages = 0;
42+
43+
/// <summary>
44+
/// A count of iterated pages thus far.
45+
/// </summary>
46+
internal int iteratedPages = 0;
47+
48+
/// <summary>
49+
/// Total number of overflow items, less than the maximum number of items in a page.
50+
/// Modulus of original page size.
51+
/// </summary>
52+
internal int overflowItemsCount = 0;
53+
54+
/// <summary>
55+
/// Total number of fetched items.
56+
/// </summary>
57+
internal int totalFetchedItems = 0;
58+
59+
/// <summary>
60+
/// Initializes paging values.
61+
/// </summary>
62+
/// <param name="invocationInfo">A reference to <see cref="System.Management.Automation.InvocationInfo"/> object.</param>
63+
/// <param name="PageSize">A reference to page size parameter.</param>
64+
public void InitializePaging(ref global::System.Management.Automation.InvocationInfo invocationInfo, ref int PageSize)
65+
{
66+
if (PageSize > MaxPageSize)
67+
{
68+
invocationInfo.BoundParameters["All"] = true;
69+
originalPageSize = PageSize;
70+
requiredPages = PageSize / DefaultPageSize;
71+
overflowItemsCount = PageSize % DefaultPageSize;
72+
invocationInfo.BoundParameters["PageSize"] = DefaultPageSize;
73+
PageSize = DefaultPageSize;
74+
}
75+
}
76+
77+
/// <summary>
78+
/// Determines whether the cmdlet should follow the OData next link url.
79+
/// </summary>
80+
/// <param name="boundParameters">The bound parameters of the cmdlet.</param>
81+
/// <param name="itemsCount">Current page items count.</param>
82+
/// <returns>True if it can iterate pages; False if it can't.</returns>
83+
public bool ShouldIteratePages(global::System.Collections.Generic.Dictionary<string, object> boundParameters, int itemsCount)
84+
{
85+
iteratedPages++;
86+
totalFetchedItems += itemsCount;
87+
if ((boundParameters.ContainsKey("All") && !boundParameters.ContainsKey("PageSize")) ||
88+
(boundParameters.ContainsKey("PageSize") && totalFetchedItems < originalPageSize))
89+
return true;
90+
else
91+
return false;
92+
}
93+
94+
/// <summary>
95+
/// Gets an OData next link for the overflow items.
96+
/// </summary>
97+
/// <param name="requestUri">The OData next link returned by the service.</param>
98+
/// <returns>An OData next link URI for the overflow items.</returns>
99+
public global::System.Uri GetOverflowItemsNextLinkUri(global::System.Uri requestUri)
100+
{
101+
var nextLinkUri = new global::System.UriBuilder(requestUri);
102+
if (requiredPages == iteratedPages && overflowItemsCount > 0)
103+
{
104+
if (nextLinkUri.Query.Contains("$top"))
105+
{
106+
global::System.Collections.Specialized.NameValueCollection queryString = global::System.Web.HttpUtility.ParseQueryString(nextLinkUri.Query);
107+
queryString["$top"] = global::System.Uri.EscapeDataString(overflowItemsCount.ToString());
108+
nextLinkUri.Query = queryString.ToString();
109+
}
110+
else
111+
{
112+
nextLinkUri.Query += $"$top=" + global::System.Uri.EscapeDataString(overflowItemsCount.ToString());
113+
}
114+
}
115+
return nextLinkUri.Uri;
116+
}
117+
}
118+
}

0 commit comments

Comments
 (0)