Solved

Using a nested REST API in combination with pagination (using the Graph API)

  • 22 May 2023
  • 2 replies
  • 226 views

Hello everyone,

 

I hope someone can help me with this.

I am working on getting Azure AD Groups via the Graph API and then retrieving the members through a nested API call.


I saw a post concerning a nested REST API call together with pagination in which one of the commenters (Gijs) had the exact same API which I am also using (commented by Gijs):
https://support.timextender.com/rsd-file-customization-96/using-a-nested-rest-api-in-combination-with-paging-858

However, the suggestion didn't work for my use-case as it only ‘expanded’ the nested values, but didn't paginate. I only got the members of the first 100 groups.



I used the RSD-file from the commenter Gijs with the addition of two points:

  • two additional rows to enable pagination;
  • [memberout.userPrincipalName| allownull()] instead of [memberout.userPrincipalName] as I was getting a [500] error with this specific attribute.

I've made them bold in the code down below.


Would someone be able to give me some pointers to what I'm missing in the RSD file to enable pagination on this nested REST API call?

Kind regards,
Johnny Yang

This is what I have written in the RSD file:

 

<api:script xmlns:api="http://apiscript.com/ns?v1" xmlns:xs="http://www.w3.org/2001/XMLSchema">

  <!-- See Column Definitions to specify column behavior and use XPaths to extract column values from JSON. -->
  <api:info title="GraphGroupMembers_Test2" desc="Generated schema file." xmlns:other="http://apiscript.com/ns?v1">
    <!-- You can modify the name, type, and column size here. -->
    <attr name="GroupId"           xs:type="string"  readonly="false" other:xPath="groupId"            />
    <attr name="GroupDisplayName"  xs:type="string"  readonly="false" other:xPath="groupDisplayName"   />
    <attr name="GroupDescription"  xs:type="string"  readonly="false" other:xPath="groupDescription"   />
    <attr name="UserId"            xs:type="string"  readonly="false" other:xPath="userId"             />
    <attr name="UserDisplayName"   xs:type="string"  readonly="false" other:xPath="userDisplayName"    />
    <attr name="UserPrincipalName" xs:type="string"  readonly="false" other:xPath="userPrincipalName"  />
    <!-- Attributes are used for passing to child API call -->
    <attr name="Id"                xs:type="string"  readonly="false" other:xPath="id"                 />
    <attr name="DisplayName"       xs:type="string"  readonly="false" other:xPath="displayName"        />
    <attr name="Description"       xs:type="string"  readonly="false" other:xPath="description"        />
  </api:info>

  <api:set attr="groupin.DataModel" value="DOCUMENT" />
  <api:set attr="groupin.JSONPath" value="/value" />
  <api:set attr="groupin.URITemplate" value="https://graph.microsoft.com/v1.0/groups?$select=id,displayName,description" />
  <api:set attr="groupin.pageurlpath" value="/\[@odata.nextLink\]"     />        
  <api:set attr="groupin.RepeatElement" value="/value/"                 />     
       
 
 <api:set attr="groupin.ElementMapPath#" value="/value/id" />
  <api:set attr="groupin.ElementMapName#" value="group_id" />
  <api:set attr="groupin.EnablePaging" value="TRUE" />
  <api:set attr="memberin.DataModel" value="DOCUMENT" />
  <api:set attr="memberin.EnablePaging" value="TRUE" />
  <api:set attr="memberin.JSONPath" value="/value" />
  <api:set attr="memberin.URITemplate" value="https://graph.microsoft.com/v1.0/groups/{groupId}/members?$select=id,displayName,userPrincipalName" />
  
  
  <!--api:set attr="JSONPath" value="" /-->

  <!-- The GET method corresponds to SELECT. Here you can override the default processing of the SELECT statement. The results of processing are pushed to the schema's output. See SELECT Execution for more information. -->
  <api:script method="GET">
    <api:set attr="groupin.URI" value="[groupin.URITemplate]" />
    <!-- Get the groups -->
    <api:call op="jsonproviderGet" in="groupin" out="groupout">
      <api:set attr="memberin.groupId" value="[groupout.id]" />
      <api:set attr="memberin.groupDisplayName" value="[groupout.displayName | allownull()]" />
      <api:set attr="memberin.groupDescription" value="[groupout.description | allownull()]" />
      <api:set attr="memberin.URI" value="[memberin.URITemplate | replace('{groupId}', [memberin.groupId])]" />
      <!-- Get the members of the group -->
      <api:call op="jsonproviderGet" in="memberin" out="memberout">
        <api:set attr="out.userId" value="[memberout.id]" />
        <api:set attr="out.userDisplayName" value="[memberout.displayName]" />
        <api:set attr="out.userPrincipalName" value="[memberout.userPrincipalName| allownull()]" />
        <api:set attr="out.groupId" value="[memberin.groupId | allownull()]" />
        <api:set attr="out.groupDisplayName" value="[memberin.groupDisplayName | allownull()]" />
        <api:set attr="out.groupDescription" value="[memberin.groupDescription | allownull()]" />
        <api:push item="out"/>
        <!--api:push/-->
      </api:call>
    </api:call>
  </api:script>

...

</api:script>

icon

Best answer by Thomas Lind 17 October 2023, 16:09

View original

2 replies

Userlevel 6
Badge +5

Hi @Johnny Y 

Sorry for the delay. I think I resolved this issue, but forgot to add my result.

<api:script xmlns:api="http://apiscript.com/ns?v1" xmlns:xs="http://www.w3.org/2001/XMLSchema">

<!-- See Column Definitions to specify column behavior and use XPaths to extract column values from JSON. -->
<api:info title="Groups" desc="Generated schema file." xmlns:other="http://apiscript.com/ns?v1">
<!-- You can modify the name, type, and column size here. -->
<attr name="@odata.nextLink" xs:type="string" readonly="false" other:xPath="/json/[@odata.nextLink]" />
<attr name="securityIdentifier" xs:type="string" readonly="false" other:xPath="/json/value/securityIdentifier" />
<attr name="securityEnabled" xs:type="boolean" readonly="false" other:xPath="/json/value/securityEnabled" />
<attr name="displayName" xs:type="string" readonly="false" other:xPath="/json/value/displayName" />
<attr name="groupTypes" xs:type="string" readonly="false" other:xPath="/json/value/groupTypes" />
<attr name="id" xs:type="string" readonly="false" other:xPath="/json/value/id" />
<attr name="mail" xs:type="string" readonly="false" other:xPath="/json/value/mail" />
<attr name="mailEnabled" xs:type="boolean" readonly="false" other:xPath="/json/value/mailEnabled" />
<attr name="mailNickname" xs:type="string" readonly="false" other:xPath="/json/value/mailNickname" />
<attr name="membershipRule" xs:type="string" readonly="false" other:xPath="/json/value/membershipRule" />
<attr name="membershipRuleProcessingState" xs:type="string" readonly="false" other:xPath="/json/value/membershipRuleProcessingState" />
<attr name="visibility" xs:type="string" readonly="false" other:xPath="/json/value/visibility" />
</api:info>

<api:set attr="DataModel" value="DOCUMENT" />
<api:set attr="URI" value="https://graph.microsoft.com/v1.0/groups?$top=10" />
<api:set attr="EnablePaging" value="true" />
<api:set attr="pageurlpath" value="/\[@odata.nextLink\]" />

<api:set attr="JSONPath" value="$.value" />

<!-- The GET method corresponds to SELECT. Here you can override the default processing of the SELECT statement. The results of processing are pushed to the schema's output. See SELECT Execution for more information. -->
<api:script method="GET">
<api:set attr="method" value="GET"/>
<api:call op="jsonproviderGet">
<api:push/>
</api:call>
</api:script>

</api:script>

I do not have more than 100 groups, so I set my limit to 10 for each iteration. That is the $top=10 part.

To get the rest you will need to set it up with query slicers.

This is the users rsd that uses it.

<api:script xmlns:api="http://apiscript.com/ns?v1" xmlns:xs="http://www.w3.org/2001/XMLSchema">

<!-- See Column Definitions to specify column behavior and use XPaths to extract column values from JSON. -->
<api:info title="Users" desc="Generated schema file." xmlns:other="http://apiscript.com/ns?v1" other:queryslicercolumn="group_id">
<!-- You can modify the name, type, and column size here. -->
<attr name="@odata.nextLink" xs:type="string" readonly="false" other:xPath="/json/[@odata.nextLink]" />
<attr name="businessPhones" xs:type="string" readonly="false" other:xPath="/json/value/businessPhones" />
<attr name="displayName" xs:type="string" readonly="false" other:xPath="/json/value/displayName" />
<attr name="givenName" xs:type="string" readonly="false" other:xPath="/json/value/givenName" />
<attr name="id" xs:type="string" readonly="false" other:xPath="/json/value/id" />
<attr name="jobTitle" xs:type="string" readonly="false" other:xPath="/json/value/jobTitle" />
<attr name="mail" xs:type="string" readonly="false" other:xPath="/json/value/mail" />
<attr name="mobilePhone" xs:type="string" readonly="false" other:xPath="/json/value/mobilePhone" />
<attr name="officeLocation" xs:type="string" readonly="false" other:xPath="/json/value/officeLocation" />
<attr name="preferredLanguage" xs:type="string" readonly="false" other:xPath="/json/value/preferredLanguage" />
<attr name="surname" xs:type="string" readonly="false" other:xPath="/json/value/surname" />
<attr name="userPrincipalName" xs:type="string" readonly="false" other:xPath="/json/value/userPrincipalName" />
<input name="group_id" xs:type="string" />
</api:info>

<api:set attr="DataModel" value="DOCUMENT" />
<api:set attr="URI" value="https://graph.microsoft.com/v1.0/groups/{group_id}/members?$top=10" />
<!-- Check if next page. The next page URI will be in _input.rows@next -->
<api:match pattern="*group_id*" type="glob" value="[_query.criteria]">
<api:else>
<api:set attr="URI" value="https://graph.microsoft.com/v1.0/groups/0091e6eb-6a2f-4cd5-ba58-a74c2173f43a/members?$top=10" />
</api:else>
</api:match>
<api:set attr="JSONPath" value="$.value" />
<api:set attr="EnablePaging" value="true" />
<api:set attr="pageurlpath" value="/\[@odata.nextLink\]" />

<!-- The GET method corresponds to SELECT. Here you can override the default processing of the SELECT statement. The results of processing are pushed to the schema's output. See SELECT Execution for more information. -->
<api:script method="GET">
<api:set attr="method" value="GET"/>
<api:call op="jsonproviderGet">
<api:push/>
</api:call>
</api:script>

</api:script>

 

Userlevel 6
Badge +5

Hi @Johnny Y please let us know if the answer above resolves your issue or if you have any follow up questions. Thanks!

Reply