Skip to content

Commit 5dd7ac9

Browse files
committed
Add support for paging of collection.
1 parent 6c4b62c commit 5dd7ac9

File tree

4 files changed

+138
-9
lines changed

4 files changed

+138
-9
lines changed

src/Beta/Users.User/Users.User/Microsoft.Graph.Users.User.psd1

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
#
44
# Generated by: Microsoft Corporation
55
#
6-
# Generated on: 3/12/2020
6+
# Generated on: 5/11/2020
77
#
88

99
@{
@@ -12,13 +12,13 @@
1212
RootModule = './Microsoft.Graph.Users.User.psm1'
1313

1414
# Version number of this module.
15-
ModuleVersion = '0.2.0'
15+
ModuleVersion = '0.7.0'
1616

1717
# Supported PSEditions
1818
CompatiblePSEditions = 'Core', 'Desktop'
1919

2020
# ID used to uniquely identify this module
21-
GUID = '89a6467f-8fac-4642-bb9d-f5d784682c8b'
21+
GUID = '38118a66-314c-4095-9b3c-72f38ad12480'
2222

2323
# Author of this module
2424
Author = 'Microsoft Corporation'
@@ -51,7 +51,7 @@ DotNetFrameworkVersion = '4.7.2'
5151
# ProcessorArchitecture = ''
5252

5353
# Modules that must be imported into the global environment prior to importing this module
54-
RequiredModules = @('Microsoft.Graph.Authentication')
54+
RequiredModules = @(@{ModuleName = 'Microsoft.Graph.Authentication'; RequiredVersion = '0.5.1'; })
5555

5656
# Assemblies that must be loaded prior to importing this module
5757
RequiredAssemblies = './bin/Microsoft.Graph.Users.User.private.dll'
@@ -69,7 +69,8 @@ FormatsToProcess = './Microsoft.Graph.Users.User.format.ps1xml'
6969
# NestedModules = @()
7070

7171
# Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export.
72-
FunctionsToExport = '*'
72+
FunctionsToExport = 'Get-MgUser', 'Get-MgUserPresence', 'New-MgUser', 'Remove-MgUser',
73+
'Update-MgUser', 'Update-MgUserPresence'
7374

7475
# Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export.
7576
CmdletsToExport = @()

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 processRecordRegex = /(^\s*)(protected\s*override\s*void\s*BeginProcessing\(\)\s*{)/gmi
398+
$ = $.replace(processRecordRegex, '$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)