Forum Discussion

DLEATE's avatar
DLEATE
Frequent Visitor
4 years ago

Help Needed with Recursive Pagination based on the value of a parameter

I was wondering if anyone could help me, I am tying to pull around 3k records from Qualys API however they have a max limit of 1000 per query.

I have managed to get a working query that extracts the first 1000 records then passes the lastId back into the post parameters to start the new query at the lastId this works fine however I am really struggling with the recursion and final output.

 

I have included my code with some comments around what I would like to do, any help appreciated.

 

let

//Recursive Loop Here while hasMoreRecords = true

url = "https://qualysapi.qualys.eu/qps/rest/2.0/search/am/hostasset/",
authKey = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
Headers = [
#"Content-Type" = "text/xml",
#"Accept" = "application/xml",
#"Authorization" = "Basic " & authKey,
#"X-Requested-With" = "AHARP_BI",
#"Cache-Control" = "no-cache",
#"lastID" = lastId,
#"hasMoreRecords" = hasMoreRecords
],

postData = "<ServiceRequest><preferences><limitResults>10000</limitResults></preferences><filters><Criteria field=""tagName"" operator=""EQUALS"">Cloud Agent</Criteria><Criteria field=""id"" operator=""GREATER"">"&lastId&"</Criteria></filters></ServiceRequest>",
jsonResponse = Xml.Tables(Web.Contents(url, [Headers = Headers, Content = Text.ToBinary(postData)]))],

//End and output once hasMoreRecords = false

data = jsonResponse{0}[data],
HostAsset = data{0}[HostAsset,
#"Changed Type" = Table.TransformColumnTypes(HostAsset,{{"id", Int64.Type}, {"name", type text}, {"created", type datetime}, {"modified", type datetime}, {"type", type text}, {"qwebHostId", Int64.Type}, {"lastVulnScan", type datetime}, {"lastSystemBoot", type datetime}, {"lastLoggedOnUser", type text}, {"domain", type text}, {"fqdn", type text}, {"os", type text}, {"dnsHostName", type text}, {"netbiosName", type text}, {"networkGuid", type text}, {"address", type text}, {"trackingMethod", type text}, {"manufacturer", type text}, {"model", type text}, {"totalMemory", Int64.Type}, {"timezone", type text}, {"biosDescription", type text}, {"isDockerHost", type logical}, {"cloudProvider", type text}})
in
#"Changed Type"

 

I want to do something like this..

if hasMoreRecords = "true"

then @Query

else output all results

2 Replies

  • v-jingzhang's avatar
    v-jingzhang
    Icon for Community Support rankCommunity Support

    Hi DLEATE 

     

    I'm not able to provide detailed suggestions as I don't have much experience in this kind of recursive pagination. I found some articles and videos for related topics. Hope they would be helpful. 

    Fibonacci Sequence: Understanding the Power Query Recursive Function for Power BI 

    Start of series: Programming loops with M-language in Power Query 

    How to do Pagination without knowing the number of pages (Part 2) in Power Query | List.Generate 

    How to Use List Generate to Make API calls in Power Query 

    Iterate Over Dynamic Web API Pages With Power Query – How To Resolve Cursor-Based Pagination 

     

    Best Regards,
    Community Support Team _ Jing
    If this post helps, please Accept it as Solution to help other members find it.

  • DLEATE's avatar
    DLEATE
    Frequent Visitor

    So a Quick Update: 

    I am struggling with pagination recursion in M but I have built a manual loop with manual iterations just to make sure I had the code right... I am finding functional language fairly hard to wrap my head around!

     

    I think i need to create a function and call it whilst the variable hasMoreRecords = true but this is day 4 and my brain is literally melting.. 🙂

     

    Anyway whilst the code is crude and ugly I thought it may help someone that was struggling to create a power Query integration with Qualys.

     

    Any advice on how should be done in M is very welcome 🙂

     

    let

     

    url = "https://qualysapi.qualys.eu/qps/rest/2.0/search/am/hostasset/",
    authKey = "xxxxxxxxxxxxxxxxxxxxx",
    Headers = [
    #"Content-Type" = "text/xml",
    #"Accept" = "application/xml",
    #"Authorization" = "Basic " & authKey,
    #"X-Requested-With" = "AHARP_BI"
    //#"Cache-Control" = "no-cache",
    //#"lastID" = lastId,
    //#"lastID1" = lastId1,
    //#"hasMoreRecords" = hasMoreRecords
    ],
    //lastId1 = jsonResponse{0}[lastId],
    //Recursive Loop Here while hasMoreRecords = true

    //Manual test Loop
    postData = "<ServiceRequest><preferences><limitResults>10</limitResults></preferences><filters><Criteria field=""tagName"" operator=""EQUALS"">Cloud Agent</Criteria><Criteria field=""id"" operator=""GREATER"">0</Criteria></filters></ServiceRequest>",
    jsonResponse = Xml.Tables(Web.Contents(url, [Headers = Headers, Content = Text.ToBinary(postData)])),
    lastId1 = jsonResponse{0}[lastId],
    data = jsonResponse{0}[data],

    postData1 = "<ServiceRequest><preferences><limitResults>10</limitResults></preferences><filters><Criteria field=""tagName"" operator=""EQUALS"">Cloud Agent</Criteria><Criteria field=""id"" operator=""GREATER"">"&lastId1&"</Criteria></filters></ServiceRequest>",
    jsonResponse1 = Xml.Tables(Web.Contents(url, [Headers = Headers, Content = Text.ToBinary(postData1)])),
    lastId2 = jsonResponse1{0}[lastId],
    data1 = jsonResponse1{0}[data],

    postData2 = "<ServiceRequest><preferences><limitResults>10</limitResults></preferences><filters><Criteria field=""tagName"" operator=""EQUALS"">Cloud Agent</Criteria><Criteria field=""id"" operator=""GREATER"">"&lastId2&"</Criteria></filters></ServiceRequest>",
    jsonResponse2 = Xml.Tables(Web.Contents(url, [Headers = Headers, Content = Text.ToBinary(postData1)])),
    lastId3 = jsonResponse2{0}[lastId],
    data2 = jsonResponse2{0}[data],

    postData3 = "<ServiceRequest><preferences><limitResults>10</limitResults></preferences><filters><Criteria field=""tagName"" operator=""EQUALS"">Cloud Agent</Criteria><Criteria field=""id"" operator=""GREATER"">"&lastId3&"</Criteria></filters></ServiceRequest>",
    jsonResponse3 = Xml.Tables(Web.Contents(url, [Headers = Headers, Content = Text.ToBinary(postData1)])),
    lastIda4 = jsonResponse3{0}[lastId],
    data3 = jsonResponse3{0}[data],

    postData4 = "<ServiceRequest><preferences><limitResults>10</limitResults></preferences><filters><Criteria field=""tagName"" operator=""EQUALS"">Cloud Agent</Criteria><Criteria field=""id"" operator=""GREATER"">"&lastId4&"</Criteria></filters></ServiceRequest>",
    jsonResponse4 = Xml.Tables(Web.Contents(url, [Headers = Headers, Content = Text.ToBinary(postData1)])),
    lastId5 = jsonResponse4{0}[lastId],
    data4 = jsonResponse4{0}[data],

    postData5 = "<ServiceRequest><preferences><limitResults>10</limitResults></preferences><filters><Criteria field=""tagName"" operator=""EQUALS"">Cloud Agent</Criteria><Criteria field=""id"" operator=""GREATER"">"&lastId5&"</Criteria></filters></ServiceRequest>",
    jsonResponse5 = Xml.Tables(Web.Contents(url, [Headers = Headers, Content = Text.ToBinary(postData1)])),
    lastId6 = jsonResponse5{0}[lastId],
    data5 = jsonResponse5{0}[data],


    HostAsset = data{0}[HostAsset] & data1{0}[HostAsset] & data2{0}[HostAsset] & data3{0}[HostAsset] & data4{0}[HostAsset] & data5{0}[HostAsset],
    #"Changed Type" = Table.TransformColumnTypes(HostAsset,{{"id", Int64.Type}, {"name", type text}, {"created", type datetime}, {"modified", type datetime}, {"type", type text}, {"qwebHostId", Int64.Type}, {"lastVulnScan", type datetime}, {"lastSystemBoot", type datetime}, {"lastLoggedOnUser", type text}, {"domain", type text}, {"fqdn", type text}, {"os", type text}, {"dnsHostName", type text}, {"netbiosName", type text}, {"networkGuid", type text}, {"address", type text}, {"trackingMethod", type text}, {"manufacturer", type text}, {"model", type text}, {"totalMemory", Int64.Type}, {"timezone", type text}, {"biosDescription", type text}, {"isDockerHost", type logical}, {"cloudProvider", type text}})
    in
    #"Changed Type"