1

I want to make API calls until a condition is met. I figured I might use a while loop.

I have a JSON response from the server that is paginated.

{
    "services": [
        {
            "id": "ABC12",
            "name": "Networks",
            "description": null,
            "status": "active",
            "teams": [
                {
                    "id": "XYZ12",
                    "type": "team_reference",
                    "summary": "Network Systems ",
                }
            ],
            "acknowledgement_timeout": null,
            "auto_resolve_timeout": null,
            "alert_grouping": "intelligent",
            "alert_grouping_timeout": null,
            "integrations": [],
            "response_play": null,
            "type": "service",
            "summary": "All Events",
        }
     ],
     "limit": 25,
     "offset": 0,
     "total": null,
     "more": true
}

limit - max I can set is 100.

offset - If specified, shows results from that point.

more - If TRUE, there are more results. If FALSE, that is the end.

for more info on this pagination - https://v2.developer.pagerduty.com/docs/pagination

I need to match the name "Networks" and get its corresponding id "ABC12". The problem is, I have to paginate make multiple calls to the API.

I have written this so far.

import requests
import json
import urllib3

# Supress SSL warnings
urllib3.disable_warnings()

# API key
API_KEY = '12345asdfg'

def list_services():

    x = 25
    y = 0
    results = []

    url = f'https://api.pagerduty.com/services/?limit={x}&offset={y}'
    headers = {
        'Accept': 'application/vnd.pagerduty+json;version=2',
        'Authorization': 'Token token={token}'.format(token=API_KEY)
    }
    current_page = json.loads(requests.get(url, verify=False, headers=headers).content.decode('UTF-8'))
    results.append(current_page)

    while current_page['more'] == 'True':
        y = y + 1
        current_page = json.loads(requests.get(url, verify=False, headers=headers).content.decode('UTF-8'))
        results.append(current_page)
        print(results) # Does not print anything
    print(results) # Prints only the first call results, the while loop 
                   # doesn't seem to work.

if __name__ == '__main__':
    list_services()

the print(results) outside the while loop prints only the first API call results. The while loop doesn't seem to work. But the code compiles without any errors.

  1. how do I set the value of x to 25 and make API calls and append the results to results until more is false?

OR

  1. how do I make multiple API calls until I find the match. If I found a match, then stop making the call.

Or is there a better cleaner way to do this?

Bharath
  • 559
  • 11
  • 27
  • It looks like you generally have everything solved. You just failed to account for failed requests which can happen. See the following in regards to the raised exceptions from carrying out a requests.get call. https://stackoverflow.com/questions/16511337/correct-way-to-try-except-using-python-requests-module – Fallenreaper Aug 13 '19 at 00:10
  • Hi @Fallenreaper. thanks for your response. I have edited my question again. Sorry didn't point out what the problem was. The `print(results)` prints only 25 results, as specified in the first call. The while loop doesn't seem to work. – Bharath Aug 13 '19 at 00:22

2 Answers2

1

This does not work because you never actually reassign the url variable once y is changed. Also you are checking against 'True' which is a string, not a boolean value. In addition I believe the offset should increase by the amount of results everytime; not just one. For example if on your first call you get results 1-25. Then if you increase y by one, the second call will yield 2-26. Instead you should increase it by the limit. This way on the second call you get results 25-50. Here is how I would do this:

def list_services():

    x = 25
    y = 0
    results = []
    serv_id = None
    flag = False

    url = f'https://api.pagerduty.com/services/?limit={x}&offset={y}'
    headers = {
        'Accept': 'application/vnd.pagerduty+json;version=2',
        'Authorization': 'Token token={token}'.format(token=API_KEY)
    }
    current_page = json.loads(requests.get(url, verify=False, headers=headers).content.decode('UTF-8'))
    results.append(current_page)

    for serv_set in current_page['services']:
            if serv_set['name'] == 'Networks':
                serv_id = serv_set['id']
                flag = True

    while current_page['more'] == True and not flag:
        for serv_set in current_page['services']:
            if serv_set['name'] == 'Networks':
                serv_id = serv_set['id']
                break
        y += x
        url = f'https://api.pagerduty.com/services/?limit={x}&offset={y}'
        current_page = json.loads(requests.get(url, verify=False, headers=headers).content.decode('UTF-8'))
        results.append(current_page)
        print(results) 
    print(results, serv_id) 

You could further clean this up to avoid some redundancy but this should work. You should also check the status of the API call to ensure that you have a valid response.

Edit:

I edited in the issue dealing with obtaining the id attribute when the name == 'Networks'. Once again you could reduce the redundancy in this a lot but this will get you on the right track. Now serv_id = the id of the service with the name of Networks. If no match is found at the end of the iterations then serv_id will be None.

Robert Kearns
  • 1,631
  • 1
  • 8
  • 15
  • Hi Robert, thanks for your answer. The `current_page['services']['name'] == 'Networks'` throws a `TypeError: list indices must be integers or slices, not str`. If I take it out, the rest works perfectly fine. – Bharath Aug 13 '19 at 01:12
  • Ah yes, I did not see that the ```services``` is actually a list. I will edit in the correct way to deal with that. If this solves your question, please select the checkmark to mark it as solves. Cheers! – Robert Kearns Aug 13 '19 at 01:16
  • Yep for sure. I've accepted it. Thanks very much. Just one more thing, Sorry to bug you. I need to get the corresponding `id` of the matched `name`. Like, if `networks` is matched, I want the corresponding `id` which is `ABC12`. – Bharath Aug 13 '19 at 01:22
  • What do you want to do with it? Print it to the console? – Robert Kearns Aug 13 '19 at 01:22
  • Nah, just store it in a variable. – Bharath Aug 13 '19 at 01:23
0
while current_page['more'] == 'True':

You are checking for a string called 'True' instead of a boolean of True, as is defined in your json file. This could be why your while loop is never executing, and you are not receiving your print statement.

Also, generally for API calls that have more than 1 page of data, you need to specify which page you are getting. Which means you need to reinitialize your payload in your while loop.

For example, if an API has a parameter called "page" that you can pass in, in your while loop you would have to pass in page = 1, page = 2, etc. as a payload.

alex067
  • 3,159
  • 1
  • 11
  • 17