3

I'm trying to simply delete a few cards that were created by my app. However, it appears as though the list() method cycles through every single card in the entire user's timeline.

My code below is slightly modified from the example in the documentation under the timeline list. When I attempted to use this, it accidentally looped through every card in my timeline using up my entire 1,000 / day quota in just a few seconds before the operation timed out.

 def delete_previous_cards(self):
   """
     This cleans up any cards that may have been leftover.
   """

   result = []
   request = self.mirror_service.timeline().list()
   while request:
     try:
       timeline_items = request.execute()
       result.extend(timeline_items.get('items', []))
       request = self.mirror_service.timeline().list_next(request, timeline_items)
     except errors.HttpError, error:
       print 'An error occurred: %s' % error
       break

   for item in result:
     item_id = item['id']
     self.mirror_service.timeline().delete(id=item_id).execute()

What's the best way to efficiently delete the cards created by my app?

Sahas Katta
  • 1,716
  • 3
  • 14
  • 24
  • 1
    Your code looks correct to me. timeline list only lists cards that your Glassware has access to. This includes only cards you created or cards that were created by sharing with your Glassware. But it seems like something's up. Why do you suspect that it's listing all cards? – mimming Jun 30 '13 at 22:14
  • I'm quite confident that either (a) the code I posted is bad OR (b) the `list()` function is fetching EVERY card (not just the ones created by my app). My reasoning? My app has only created 2-3 cards and the loop should be instant. Yet, running the code above results in this: http://i.imgur.com/9LBUxWV.png I ended up accidentally using up my entire API call quota in a few seconds! – Sahas Katta Jun 30 '13 at 23:03
  • Got it. Another possibility is that the exit condition isn't being triggered. The Mirror API uses a cursor, so it's possible that you're paging through endless empty pages. You can test this by logging the number of records in each page. You might also want to add an escape hatch to this code so it exits after a few iterations (instead of all 1000 that your quota allows) – mimming Jul 01 '13 at 00:39
  • 2
    There is indeed a "bug" in the Python example for mirror.timeline.list: the client library feature can't be used with the Mirror API as the exit condition is not the same. A fix is on its way but the short answer is that you should exit when `timeline_items.get('items', [])` is empty. – Alain Jul 01 '13 at 17:29

2 Answers2

3

There's a JavaScript based tool that an Explorer wrote for just this purpose: Glass Cleaner.

mimming
  • 13,974
  • 3
  • 45
  • 74
  • +1 for glassware I wasn't aware of, note this will only delete timeline cards created by the client id / glassware app provided to the tool, Jenny is correct that despite Sahas's hypothesis he was getting all cards back in a list, that is not happening. – Mark Scheel Jul 01 '13 at 07:41
2

It looks to me like the Python example is missing any concept of a pageToken, most of the other language examples have a nextPageToken and loop until the response does not have a nextPageToken. If you keep requesting the first page over and over even if you only have three cards you will quickly exhaust your API quota.

The rest of this answer is general information about list and delete and some curl commands you can safely experiment with that won't loop and exhaust your quota quite as quickly. Make special note of the nextPageToken property in the returned JSON from list commands ...

LIST and DELETE are weird and don't follow the documentation exactly in my experience.

Here is a sample CURL command for List.

curl -x http://localhost:5671 -H "Authorization: Bearer YOUR_TOKEN_HERE" 
https://www.googleapis.com/mirror/v1/timeline

It returns 10 items for the user and app that are associated with the token.

It includes deleted items (isDeleted set to true) but does not show the isDeleted property in the output JSON. This is weird.

If you modify it slightly:

curl -x http://localhost:5671 -H "Authorization: Bearer YOUR_TOKEN_HERE" 
https://www.googleapis.com/mirror/v1/timeline?isDeleted=true

(note the trailing parameter) now you get the same list but the output JSON includes the isDeleted property. The lesson for me here is you should probably be requesting isDeleted=false for looping delete requests.

To delete an item you can do this:

curl -x http://localhost:5671 -H "Authorization: Bearer YOUR_TOKEN_HERE" 
-H "Content-Type: application/json" -v -X DELETE 
https://www.googleapis.com/mirror/v1/timeline/ID_OF_A_TIMELINE_CARD

Note you have to use an actual id from a card you got from a list command at the end. Grab one from a list command above.

When you do a successful DELETE the response is a 204, which in a RESTful world can indicate delete success.

Then if you do a subsequent list as in the first example above the item will come right back and not be marked as deleted because the isDeleted property is missing.

Pages seem to be 10 in size, but I guess that could change, since I didn't find that documented anywhere.

nextPageToken values seem to frequently have identical beginnings and ends, and they are very long strings, so it can be confusing to look at them and you might inadvertently think they are identical when they are not, lesson here is to compare very carefully in the middle.

Maybe those curl commands help you experiment when your API quota comes back, and I would experiment with testing for a null or empty string nextPageToken to tell you when to exit your loop. The equivalent java code is:

} while (request.getPageToken() != null && request.getPageToken().length() > 0);

Good luck, and great question.

Community
  • 1
  • 1
Mark Scheel
  • 2,963
  • 1
  • 20
  • 23