diff --git a/autotests/calendar/data/calendar1_create_request.txt b/autotests/calendar/data/calendar1_create_request.txt index 5f8f192..b357b51 100644 --- a/autotests/calendar/data/calendar1_create_request.txt +++ b/autotests/calendar/data/calendar1_create_request.txt @@ -1,9 +1,9 @@ -POST https://www.googleapis.com/calendar/v3/calendars +POST https://www.googleapis.com/calendar/v3/calendars?prettyPrint=false Content-Type: application/json { "description": "Calendar with airdates of TV shows episodes", "location": "", "summary": "TV Shows Calendar", "timeZone": "UTC" } diff --git a/autotests/calendar/data/calendar1_fetch_request.txt b/autotests/calendar/data/calendar1_fetch_request.txt index aa9d4ee..e435376 100644 --- a/autotests/calendar/data/calendar1_fetch_request.txt +++ b/autotests/calendar/data/calendar1_fetch_request.txt @@ -1 +1 @@ -GET https://www.googleapis.com/calendar/v3/users/me/calendarList/averylongtvshowscalendarid@group.calendar.google.com +GET https://www.googleapis.com/calendar/v3/users/me/calendarList/averylongtvshowscalendarid@group.calendar.google.com?prettyPrint=false diff --git a/autotests/calendar/data/calendar1_modify_request.txt b/autotests/calendar/data/calendar1_modify_request.txt index 22d03e2..35568d9 100644 --- a/autotests/calendar/data/calendar1_modify_request.txt +++ b/autotests/calendar/data/calendar1_modify_request.txt @@ -1,10 +1,10 @@ -PUT https://www.googleapis.com/calendar/v3/calendars/averylongtvshowscalendarid@group.calendar.google.com +PUT https://www.googleapis.com/calendar/v3/calendars/averylongtvshowscalendarid@group.calendar.google.com?prettyPrint=false Content-Type: application/json { "description": "Calendar with airdates of TV shows episodes", "id": "averylongtvshowscalendarid@group.calendar.google.com", "location": "", "summary": "TV Shows Calendar", "timeZone": "UTC" } diff --git a/autotests/calendar/data/calendar2_create_request.txt b/autotests/calendar/data/calendar2_create_request.txt index 336d17c..531ad7a 100644 --- a/autotests/calendar/data/calendar2_create_request.txt +++ b/autotests/calendar/data/calendar2_create_request.txt @@ -1,10 +1,10 @@ -POST https://www.googleapis.com/calendar/v3/calendars +POST https://www.googleapis.com/calendar/v3/calendars?prettyPrint=false Content-Type: application/json { "description": "", "location": "", "summary": "MockAccount", "timeZone": "Europe/Prague" } diff --git a/autotests/calendar/data/calendar2_modify_request.txt b/autotests/calendar/data/calendar2_modify_request.txt index bf1c09b..2d3e705 100644 --- a/autotests/calendar/data/calendar2_modify_request.txt +++ b/autotests/calendar/data/calendar2_modify_request.txt @@ -1,10 +1,10 @@ -PUT https://www.googleapis.com/calendar/v3/calendars/MockAccount +PUT https://www.googleapis.com/calendar/v3/calendars/MockAccount?prettyPrint=false Content-Type: application/json { "description": "", "id": "MockAccount", "location": "", "summary": "MockAccount", "timeZone": "Europe/Prague" } diff --git a/autotests/calendar/data/calendars_fetch_page1_request.txt b/autotests/calendar/data/calendars_fetch_page1_request.txt index 0e2a1d2..0d524d7 100644 --- a/autotests/calendar/data/calendars_fetch_page1_request.txt +++ b/autotests/calendar/data/calendars_fetch_page1_request.txt @@ -1 +1 @@ -GET https://www.googleapis.com/calendar/v3/users/me/calendarList +GET https://www.googleapis.com/calendar/v3/users/me/calendarList?prettyPrint=false diff --git a/autotests/calendar/data/calendars_fetch_page2_request.txt b/autotests/calendar/data/calendars_fetch_page2_request.txt index 9b7331d..15cc7c1 100644 --- a/autotests/calendar/data/calendars_fetch_page2_request.txt +++ b/autotests/calendar/data/calendars_fetch_page2_request.txt @@ -1 +1 @@ -GET https://www.googleapis.com/calendar/v3/users/me/calendarList?pageToken=Eh0KEmc6MDAwMDAwMTQyNjVlYWI3MhDond_S9JTaAg%3D%3D +GET https://www.googleapis.com/calendar/v3/users/me/calendarList?pageToken=Eh0KEmc6MDAwMDAwMTQyNjVlYWI3MhDond_S9JTaAg%3D%3D&prettyPrint=false diff --git a/autotests/calendar/data/event1_create_request.txt b/autotests/calendar/data/event1_create_request.txt index 1dc3b46..66867e0 100644 --- a/autotests/calendar/data/event1_create_request.txt +++ b/autotests/calendar/data/event1_create_request.txt @@ -1,44 +1,44 @@ -POST https://www.googleapis.com/calendar/v3/calendars/MockAccount/events?sendUpdates=all +POST https://www.googleapis.com/calendar/v3/calendars/MockAccount/events?sendUpdates=all&prettyPrint=false Content-Type: application/json { "attendees": [ { "displayName": "", "email": "attendee1@kde.test", "id": "1234567890", "responseStatus": "needsAction" }, { "displayName": "", "email": "attendee2@kde.test", "id": "0987654321", "responseStatus": "needsAction" } ], "description": "We shall meet and we shall discuss.", "end": { "dateTime": "2018-04-01T09:30:00Z", "timeZone": "UTC+02:00" }, "iCalUID": "3if6lf59tove1e037baa75l54t@google.com", "kind": "calendar#event", "location": "Meeting Room", "organizer": { "displayName": "Konqui ", "email": "konqui@kde.test" }, "reminders": { "overrides": [ ], "useDefault": false }, "start": { "dateTime": "2018-04-01T08:30:00Z", "timeZone": "UTC+02:00" }, "status": "confirmed", "summary": "Cool Meeting about stuff", "transparency": "opaque" } diff --git a/autotests/calendar/data/event1_fetch_request.txt b/autotests/calendar/data/event1_fetch_request.txt index ef25647..e6f6bb0 100644 --- a/autotests/calendar/data/event1_fetch_request.txt +++ b/autotests/calendar/data/event1_fetch_request.txt @@ -1 +1 @@ -GET https://www.googleapis.com/calendar/v3/calendars/MockAccount/events/3if6lf59tove1e037baa75l54t +GET https://www.googleapis.com/calendar/v3/calendars/MockAccount/events/3if6lf59tove1e037baa75l54t?prettyPrint=false diff --git a/autotests/calendar/data/event1_modify_request.txt b/autotests/calendar/data/event1_modify_request.txt index 244cc29..ecdf8e1 100644 --- a/autotests/calendar/data/event1_modify_request.txt +++ b/autotests/calendar/data/event1_modify_request.txt @@ -1,45 +1,45 @@ -PUT https://www.googleapis.com/calendar/v3/calendars/MockAccount/events/3if6lf59tove1e037baa75l54t?sendUpdates=all +PUT https://www.googleapis.com/calendar/v3/calendars/MockAccount/events/3if6lf59tove1e037baa75l54t?sendUpdates=all&prettyPrint=false Content-Type: application/json { "attendees": [ { "displayName": "", "email": "attendee1@kde.test", "id": "1234567890", "responseStatus": "needsAction" }, { "displayName": "", "email": "attendee2@kde.test", "id": "0987654321", "responseStatus": "needsAction" } ], "description": "We shall meet and we shall discuss.", "end": { "dateTime": "2018-04-01T09:30:00Z", "timeZone": "UTC+02:00" }, "iCalUID": "3if6lf59tove1e037baa75l54t@google.com", "id": "3if6lf59tove1e037baa75l54t", "kind": "calendar#event", "location": "Meeting Room", "organizer": { "displayName": "Konqui ", "email": "konqui@kde.test" }, "reminders": { "overrides": [ ], "useDefault": false }, "start": { "dateTime": "2018-04-01T08:30:00Z", "timeZone": "UTC+02:00" }, "status": "confirmed", "summary": "Cool Meeting about stuff", "transparency": "opaque" } diff --git a/autotests/calendar/data/event2_create_request.txt b/autotests/calendar/data/event2_create_request.txt index 5dea677..c35c439 100644 --- a/autotests/calendar/data/event2_create_request.txt +++ b/autotests/calendar/data/event2_create_request.txt @@ -1,46 +1,46 @@ -POST https://www.googleapis.com/calendar/v3/calendars/MockAccount/events?sendUpdates=all +POST https://www.googleapis.com/calendar/v3/calendars/MockAccount/events?sendUpdates=all&prettyPrint=false Content-Type: application/json { "attendees": [ { "displayName": "KDE Hacker 1", "email": "hacker1@kde.test", "id": "1029384756", "responseStatus": "needsAction" } ], "description": "Let's hack on KDE!", "end": { "date": "2018-04-23" }, "iCalUID": "009c2cc9-0781-482d-8ccd-8fc9bfeb3138", "kind": "calendar#event", "location": "Toulouse, France", "organizer": { "displayName": "KDE Hacker 0 ", "email": "hacker@kde.test" }, "recurrence": [ "RRULE:FREQ=YEARLY;BYMONTHDAY=20;BYMONTH=4" ], "reminders": { "overrides": [ { "method": "email", "minutes": 430 }, { "method": "popup", "minutes": 10 } ], "useDefault": false }, "start": { "date": "2018-04-20" }, "status": "confirmed", "summary": "KDE PIM Sprint", "transparency": "opaque" } diff --git a/autotests/calendar/data/event2_modify_request.txt b/autotests/calendar/data/event2_modify_request.txt index 78ef1e1..45bac65 100644 --- a/autotests/calendar/data/event2_modify_request.txt +++ b/autotests/calendar/data/event2_modify_request.txt @@ -1,47 +1,47 @@ -PUT https://www.googleapis.com/calendar/v3/calendars/MockAccount/events/_60o3iopicdhjib9g6ss32b9k70p68b9ocdhm8b9ocphjioj6clh36c9j70?sendUpdates=all +PUT https://www.googleapis.com/calendar/v3/calendars/MockAccount/events/_60o3iopicdhjib9g6ss32b9k70p68b9ocdhm8b9ocphjioj6clh36c9j70?sendUpdates=all&prettyPrint=false Content-Type: application/json { "attendees": [ { "displayName": "KDE Hacker 1", "email": "hacker1@kde.test", "id": "1029384756", "responseStatus": "needsAction" } ], "description": "Let's hack on KDE!", "end": { "date": "2018-04-23" }, "iCalUID": "009c2cc9-0781-482d-8ccd-8fc9bfeb3138", "id": "_60o3iopicdhjib9g6ss32b9k70p68b9ocdhm8b9ocphjioj6clh36c9j70", "kind": "calendar#event", "location": "Toulouse, France", "organizer": { "displayName": "KDE Hacker 0 ", "email": "hacker@kde.test" }, "recurrence": [ "RRULE:FREQ=YEARLY;BYMONTHDAY=20;BYMONTH=4" ], "reminders": { "overrides": [ { "method": "email", "minutes": 430 }, { "method": "popup", "minutes": 10 } ], "useDefault": false }, "start": { "date": "2018-04-20" }, "status": "confirmed", "summary": "KDE PIM Sprint", "transparency": "opaque" } diff --git a/autotests/calendar/data/events_fetch_page1_request.txt b/autotests/calendar/data/events_fetch_page1_request.txt index ee58945..1605aa2 100644 --- a/autotests/calendar/data/events_fetch_page1_request.txt +++ b/autotests/calendar/data/events_fetch_page1_request.txt @@ -1 +1 @@ -GET https://www.googleapis.com/calendar/v3/calendars/MockAccount/events?showDeleted=true +GET https://www.googleapis.com/calendar/v3/calendars/MockAccount/events?showDeleted=true&prettyPrint=false diff --git a/autotests/calendar/data/events_fetch_page2_request.txt b/autotests/calendar/data/events_fetch_page2_request.txt index fe12564..f59748e 100644 --- a/autotests/calendar/data/events_fetch_page2_request.txt +++ b/autotests/calendar/data/events_fetch_page2_request.txt @@ -1 +1 @@ -GET https://www.googleapis.com/calendar/v3/calendars/MockAccount/events?showDeleted=true&pageToken=ClEKQ182a3MzaWRoazZvcWpnYzlsNnNyM2NjcGc2OHBqMmUyMGRsbm00cWJjY2xwbmlyajM1cGptdXJyN2RoaWlzb3JmZGsYASCAgIDqg5PLwhIaDQgAEgAY-KP05dmb2gI%3D +GET https://www.googleapis.com/calendar/v3/calendars/MockAccount/events?showDeleted=true&prettyPrint=false&pageToken=ClEKQ182a3MzaWRoazZvcWpnYzlsNnNyM2NjcGc2OHBqMmUyMGRsbm00cWJjY2xwbmlyajM1cGptdXJyN2RoaWlzb3JmZGsYASCAgIDqg5PLwhIaDQgAEgAY-KP05dmb2gI%3D diff --git a/autotests/calendar/data/freebusy1_query_request.txt b/autotests/calendar/data/freebusy1_query_request.txt index 2ff7b96..0ee45a3 100644 --- a/autotests/calendar/data/freebusy1_query_request.txt +++ b/autotests/calendar/data/freebusy1_query_request.txt @@ -1,12 +1,12 @@ -POST https://www.googleapis.com/calendar/v3/freeBusy +POST https://www.googleapis.com/calendar/v3/freeBusy?prettyPrint=false Content-Type: application/json { "items": [ { "id": "MockAccount" } ], "timeMax": "2018-04-02T14:00:00Z", "timeMin": "2018-04-01T08:00:00Z" } diff --git a/autotests/contacts/data/contact1_create_request.txt b/autotests/contacts/data/contact1_create_request.txt index 68cb103..077d3bd 100644 --- a/autotests/contacts/data/contact1_create_request.txt +++ b/autotests/contacts/data/contact1_create_request.txt @@ -1,37 +1,37 @@ -POST https://www.google.com/m8/feeds/contacts/MockAccount/full +POST https://www.google.com/m8/feeds/contacts/MockAccount/full?prettyPrint=false GData-Version: 3.0 Content-Type: application/atom+xml John Doe John Doe John is awesome! KDEdeveloper Johnnyboy Joanna Doe 987654321 123456789 Internet KDE Road 15 18000 World KDE Road 15 Internet, 18000 WORLD diff --git a/autotests/contacts/data/contact1_fetch_photo_request.txt b/autotests/contacts/data/contact1_fetch_photo_request.txt index 16e379e..3c53f35 100644 --- a/autotests/contacts/data/contact1_fetch_photo_request.txt +++ b/autotests/contacts/data/contact1_fetch_photo_request.txt @@ -1,2 +1,2 @@ -GET https://www.google.com/m8/feeds/photos/media/MockAccount/68f6ee7a8a57c4cc +GET https://www.google.com/m8/feeds/photos/media/MockAccount/68f6ee7a8a57c4cc?prettyPrint=false GData-Version: 3.0 diff --git a/autotests/contacts/data/contact1_fetch_request.txt b/autotests/contacts/data/contact1_fetch_request.txt index 36dbabd..92183f5 100644 --- a/autotests/contacts/data/contact1_fetch_request.txt +++ b/autotests/contacts/data/contact1_fetch_request.txt @@ -1,2 +1,2 @@ -GET https://www.google.com/m8/feeds/contacts/MockAccount/full/68f6ee7a8a57c4cc?alt=json +GET https://www.google.com/m8/feeds/contacts/MockAccount/full/68f6ee7a8a57c4cc?alt=json&prettyPrint=false GData-Version: 3.0 diff --git a/autotests/contacts/data/contact1_modify_request.txt b/autotests/contacts/data/contact1_modify_request.txt index d1d6af8..56b28ac 100644 --- a/autotests/contacts/data/contact1_modify_request.txt +++ b/autotests/contacts/data/contact1_modify_request.txt @@ -1,39 +1,39 @@ -PUT https://www.google.com/m8/feeds/contacts/MockAccount/full/68f6ee7a8a57c4cc +PUT https://www.google.com/m8/feeds/contacts/MockAccount/full/68f6ee7a8a57c4cc?prettyPrint=false GData-Version: 3.0 If-Match: * Content-Type: application/atom+xml John Doe John Doe John is awesome! KDEdeveloper Johnnyboy Joanna Doe 987654321 123456789 Internet KDE Road 15 18000 World KDE Road 15 Internet, 18000 WORLD diff --git a/autotests/contacts/data/contact2_create_photo_request.txt b/autotests/contacts/data/contact2_create_photo_request.txt index 6ad78d2..b9f83c3 100644 Binary files a/autotests/contacts/data/contact2_create_photo_request.txt and b/autotests/contacts/data/contact2_create_photo_request.txt differ diff --git a/autotests/contacts/data/contact2_create_request.txt b/autotests/contacts/data/contact2_create_request.txt index 8e882da..313f730 100644 --- a/autotests/contacts/data/contact2_create_request.txt +++ b/autotests/contacts/data/contact2_create_request.txt @@ -1,17 +1,17 @@ -POST https://www.google.com/m8/feeds/contacts/MockAccount/full +POST https://www.google.com/m8/feeds/contacts/MockAccount/full?prettyPrint=false GData-Version: 3.0 Content-Type: application/atom+xml Konqui Konqui KDE Mascot diff --git a/autotests/contacts/data/contact2_fetch_photo_request.txt b/autotests/contacts/data/contact2_fetch_photo_request.txt index e04d50c..a58075f 100644 --- a/autotests/contacts/data/contact2_fetch_photo_request.txt +++ b/autotests/contacts/data/contact2_fetch_photo_request.txt @@ -1,2 +1,2 @@ -GET https://www.google.com/m8/feeds/photos/media/MockAccount/2d71e4bb897f47a8 +GET https://www.google.com/m8/feeds/photos/media/MockAccount/2d71e4bb897f47a8?prettyPrint=false GData-Version: 3.0 diff --git a/autotests/contacts/data/contact2_modify_request.txt b/autotests/contacts/data/contact2_modify_request.txt index a14929f..e957ee7 100644 --- a/autotests/contacts/data/contact2_modify_request.txt +++ b/autotests/contacts/data/contact2_modify_request.txt @@ -1,18 +1,18 @@ -PUT https://www.google.com/m8/feeds/contacts/MockAccount/full/2d71e4bb897f47a8 +PUT https://www.google.com/m8/feeds/contacts/MockAccount/full/2d71e4bb897f47a8?prettyPrint=false GData-Version: 3.0 Content-Type: application/atom+xml If-Match: * Konqui Konqui KDE Mascot diff --git a/autotests/contacts/data/contacts_fetch_page1_request.txt b/autotests/contacts/data/contacts_fetch_page1_request.txt index 91780f3..58ce601 100644 --- a/autotests/contacts/data/contacts_fetch_page1_request.txt +++ b/autotests/contacts/data/contacts_fetch_page1_request.txt @@ -1,2 +1,2 @@ -GET https://www.google.com/m8/feeds/contacts/MockAccount/full?alt=json&showdeleted=true +GET https://www.google.com/m8/feeds/contacts/MockAccount/full?alt=json&showdeleted=true&prettyPrint=false Gdata-version: 3.0 diff --git a/autotests/contacts/data/contacts_fetch_page2_request.txt b/autotests/contacts/data/contacts_fetch_page2_request.txt index 36e3993..9a2f84c 100644 --- a/autotests/contacts/data/contacts_fetch_page2_request.txt +++ b/autotests/contacts/data/contacts_fetch_page2_request.txt @@ -1,2 +1,2 @@ -GET https://www.google.com/m8/feeds/contacts/MockAccount/full?max-results=1&showdeleted=true&start-index=2&alt=json +GET https://www.google.com/m8/feeds/contacts/MockAccount/full?max-results=1&showdeleted=true&start-index=2&alt=json&prettyPrint=false Gdata-version: 3.0 diff --git a/autotests/contacts/data/contacts_group1_create_request.txt b/autotests/contacts/data/contacts_group1_create_request.txt index 0c6c016..54fe343 100644 --- a/autotests/contacts/data/contacts_group1_create_request.txt +++ b/autotests/contacts/data/contacts_group1_create_request.txt @@ -1,9 +1,9 @@ -POST https://www.google.com/m8/feeds/groups/MockAccount/full +POST https://www.google.com/m8/feeds/groups/MockAccount/full?prettyPrint=false Content-Type: application/atom+xml GData-Version: 3.0 Best Friends Best Friends diff --git a/autotests/contacts/data/contacts_group1_fetch_request.txt b/autotests/contacts/data/contacts_group1_fetch_request.txt index aee99c9..1d20452 100644 --- a/autotests/contacts/data/contacts_group1_fetch_request.txt +++ b/autotests/contacts/data/contacts_group1_fetch_request.txt @@ -1,3 +1,3 @@ -GET https://www.google.com/m8/feeds/groups/MockAccount/full?alt=json +GET https://www.google.com/m8/feeds/groups/MockAccount/full?alt=json&prettyPrint=false GData-Version: 3.0 diff --git a/autotests/contacts/data/contacts_group1_modify_request.txt b/autotests/contacts/data/contacts_group1_modify_request.txt index ec4750a..dc67059 100644 --- a/autotests/contacts/data/contacts_group1_modify_request.txt +++ b/autotests/contacts/data/contacts_group1_modify_request.txt @@ -1,10 +1,10 @@ -POST https://www.google.com/m8/feeds/groups/MockAccount/full/4ca7b44c093feab0 +POST https://www.google.com/m8/feeds/groups/MockAccount/full/4ca7b44c093feab0?prettyPrint=false Content-Type: application/atom+xml GData-Version: 3.0 If-Match: * Best Friends Best Friends diff --git a/autotests/contacts/data/contacts_group2_create_request.txt b/autotests/contacts/data/contacts_group2_create_request.txt index 67692f9..bbbf861 100644 --- a/autotests/contacts/data/contacts_group2_create_request.txt +++ b/autotests/contacts/data/contacts_group2_create_request.txt @@ -1,9 +1,9 @@ -POST https://www.google.com/m8/feeds/groups/MockAccount/full +POST https://www.google.com/m8/feeds/groups/MockAccount/full?prettyPrint=false Content-Type: application/atom+xml GData-Version: 3.0 Starred in Android Starred in Android diff --git a/autotests/contacts/data/contacts_group2_modify_request.txt b/autotests/contacts/data/contacts_group2_modify_request.txt index 39155ce..ad03753 100644 --- a/autotests/contacts/data/contacts_group2_modify_request.txt +++ b/autotests/contacts/data/contacts_group2_modify_request.txt @@ -1,11 +1,11 @@ -POST https://www.google.com/m8/feeds/groups/MockAccount/full/12881c7f0a03cd16 +POST https://www.google.com/m8/feeds/groups/MockAccount/full/12881c7f0a03cd16?prettyPrint=false Content-Type: application/atom+xml GData-Version: 3.0 If-Match: * Starred in Android Starred in Android diff --git a/autotests/contacts/data/contacts_groups_fetch_page1_request.txt b/autotests/contacts/data/contacts_groups_fetch_page1_request.txt index 7fbd800..b00d864 100644 --- a/autotests/contacts/data/contacts_groups_fetch_page1_request.txt +++ b/autotests/contacts/data/contacts_groups_fetch_page1_request.txt @@ -1,2 +1,2 @@ -GET https://www.google.com/m8/feeds/groups/MockAccount/full?alt=json +GET https://www.google.com/m8/feeds/groups/MockAccount/full?alt=json&prettyPrint=false GData-Version: 3.0 diff --git a/autotests/contacts/data/contacts_groups_fetch_page2_request.txt b/autotests/contacts/data/contacts_groups_fetch_page2_request.txt index 00733ba..63648f2 100644 --- a/autotests/contacts/data/contacts_groups_fetch_page2_request.txt +++ b/autotests/contacts/data/contacts_groups_fetch_page2_request.txt @@ -1,2 +1,2 @@ -GET https://www.google.com/m8/feeds/groups/MockAccount/full?alt=json&start-index=2 +GET https://www.google.com/m8/feeds/groups/MockAccount/full?alt=json&start-index=2&prettyPrint=false GData-Version: 3.0 diff --git a/autotests/core/createjobtest.cpp b/autotests/core/createjobtest.cpp index b89e189..6306c00 100644 --- a/autotests/core/createjobtest.cpp +++ b/autotests/core/createjobtest.cpp @@ -1,120 +1,120 @@ /* * Copyright (C) 2018 Daniel Vrátil * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #include #include #include "fakenetworkaccessmanagerfactory.h" #include "testutils.h" #include "createjob.h" #include "account.h" Q_DECLARE_METATYPE(QList) using Scenarios = QList; using namespace KGAPI2; class TestCreateJob : public CreateJob { Q_OBJECT public: TestCreateJob(const QUrl &url, const QByteArray &data, QObject *parent = nullptr) : CreateJob(AccountPtr::create(QStringLiteral("MockAccount"), QStringLiteral("MockToken")), parent) , mUrl(url) , mData(data) { } void start() override { QNetworkRequest request(mUrl); enqueueRequest(request, mData); } QByteArray response() { return mResponse; } void handleReply(const QNetworkReply *, const QByteArray &rawData) override { mResponse = rawData; emitFinished(); } private: QUrl mUrl; QByteArray mData; QByteArray mResponse; }; class CreateJobTest : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase() { NetworkAccessManagerFactory::setFactory(new FakeNetworkAccessManagerFactory); } void testCreate_data() { QTest::addColumn>("scenarios"); QTest::newRow("success") << Scenarios{ - { QUrl(QStringLiteral("https://example.test/request/data")), QNetworkAccessManager::PostOperation, + { QUrl(QStringLiteral("https://example.test/request/data?prettyPrint=false")), QNetworkAccessManager::PostOperation, "New data", 200, "Data created" } }; QTest::newRow("creation failed") << Scenarios{ - { QUrl(QStringLiteral("https://example.test/request/data")), QNetworkAccessManager::PostOperation, + { QUrl(QStringLiteral("https://example.test/request/data?prettyPrint=false")), QNetworkAccessManager::PostOperation, "New data", KGAPI2::Forbidden, {} } }; QTest::newRow("redirect") << Scenarios{ - { QUrl(QStringLiteral("https://example.test/request/data")), QNetworkAccessManager::PostOperation, + { QUrl(QStringLiteral("https://example.test/request/data?prettyPrint=false")), QNetworkAccessManager::PostOperation, "New data", KGAPI2::TemporarilyMoved, "https://example.test/moved/data" }, - { QUrl(QStringLiteral("https://example.test/moved/data")), QNetworkAccessManager::PostOperation, + { QUrl(QStringLiteral("https://example.test/moved/data?prettyPrint=false")), QNetworkAccessManager::PostOperation, "New data", KGAPI2::OK, "Data created" } }; } void testCreate() { QFETCH(QList, scenarios); FakeNetworkAccessManagerFactory::get()->setScenarios(scenarios); auto job = new TestCreateJob(scenarios.first().requestUrl, scenarios.first().requestData); QVERIFY(execJob(job)); QCOMPARE(static_cast(job->error()), scenarios.last().responseCode == 200 ? KGAPI2::NoError : scenarios.last().responseCode); QCOMPARE(job->response(), scenarios.last().responseData); QVERIFY(!FakeNetworkAccessManagerFactory::get()->hasScenario()); } }; QTEST_GUILESS_MAIN(CreateJobTest) #include "createjobtest.moc" diff --git a/autotests/core/data/accountinfo_fetch_request.txt b/autotests/core/data/accountinfo_fetch_request.txt index e1de3f0..44c5206 100644 --- a/autotests/core/data/accountinfo_fetch_request.txt +++ b/autotests/core/data/accountinfo_fetch_request.txt @@ -1 +1 @@ -GET https://www.googleapis.com/oauth2/v1/userinfo +GET https://www.googleapis.com/oauth2/v1/userinfo?prettyPrint=false diff --git a/autotests/core/data/accountmanager_part1_request.txt b/autotests/core/data/accountmanager_part1_request.txt index 4ed87ac..3984957 100644 --- a/autotests/core/data/accountmanager_part1_request.txt +++ b/autotests/core/data/accountmanager_part1_request.txt @@ -1,4 +1,4 @@ -POST https://accounts.google.com/o/oauth2/token +POST https://accounts.google.com/o/oauth2/token?prettyPrint=false Content-Type: application/x-www-form-urlencoded client_id=Key1&client_secret=Secret1&code=TheCakeIsALie&redirect_uri=http://127.0.0.1:42413&grant_type=authorization_code \ No newline at end of file diff --git a/autotests/core/data/accountmanager_refresh_request.txt b/autotests/core/data/accountmanager_refresh_request.txt index 8e89929..51986e8 100644 --- a/autotests/core/data/accountmanager_refresh_request.txt +++ b/autotests/core/data/accountmanager_refresh_request.txt @@ -1,4 +1,4 @@ -POST https://accounts.google.com/o/oauth2/token +POST https://accounts.google.com/o/oauth2/token?prettyPrint=false Content-Type: application/x-www-form-urlencoded client_id=Key1&client_secret=Secret1&refresh_token=FakeRefreshToken&grant_type=refresh_token \ No newline at end of file diff --git a/autotests/core/fetchjobtest.cpp b/autotests/core/fetchjobtest.cpp index a098c55..c5b012c 100644 --- a/autotests/core/fetchjobtest.cpp +++ b/autotests/core/fetchjobtest.cpp @@ -1,154 +1,154 @@ /* * Copyright (C) 2018 Daniel Vrátil * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #include #include #include "fakenetworkaccessmanagerfactory.h" #include "testutils.h" #include "fetchjob.h" #include "account.h" Q_DECLARE_METATYPE(QList) using Scenarios = QList; using namespace KGAPI2; class TestFetchJob : public FetchJob { Q_OBJECT public: explicit TestFetchJob(const QUrl &url, QObject *parent = nullptr) : FetchJob(parent) , mUrl(url) { } TestFetchJob(const AccountPtr &account, const QUrl &url, QObject *parent = nullptr) : FetchJob(account, parent) , mUrl(url) { } void start() override { QNetworkRequest request(mUrl); enqueueRequest(request); } QByteArray response() { return mResponse; } void handleReply(const QNetworkReply *, const QByteArray &rawData) override { mResponse = rawData; emitFinished(); } private: QUrl mUrl; QByteArray mResponse; }; class FetchJobTest : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase() { NetworkAccessManagerFactory::setFactory(new FakeNetworkAccessManagerFactory); } void testUnauthenticatedFetch_data() { QTest::addColumn>("scenarios"); QTest::newRow("success") << Scenarios{ - { QUrl(QStringLiteral("https://example.test/request/data")), QNetworkAccessManager::GetOperation, + { QUrl(QStringLiteral("https://example.test/request/data?prettyPrint=false")), QNetworkAccessManager::GetOperation, {}, 200, "Test Response", false } }; QTest::newRow("not found") << Scenarios{ - { QUrl(QStringLiteral("https://example.test/does/not/exist")), QNetworkAccessManager::GetOperation, + { QUrl(QStringLiteral("https://example.test/does/not/exist?prettyPrint=false")), QNetworkAccessManager::GetOperation, {}, KGAPI2::NotFound, {}, false } }; QTest::newRow("redirect") << Scenarios{ - { QUrl(QStringLiteral("https://example.test/request/data")), QNetworkAccessManager::GetOperation, + { QUrl(QStringLiteral("https://example.test/request/data?prettyPrint=false")), QNetworkAccessManager::GetOperation, {}, KGAPI2::TemporarilyMoved, "https://example.test/moved/data", false }, - { QUrl(QStringLiteral("https://example.test/moved/data")), QNetworkAccessManager::GetOperation, + { QUrl(QStringLiteral("https://example.test/moved/data?prettyPrint=false")), QNetworkAccessManager::GetOperation, {}, KGAPI2::OK, "Here's your data", false } }; } void testUnauthenticatedFetch() { QFETCH(QList, scenarios); FakeNetworkAccessManagerFactory::get()->setScenarios(scenarios); auto job = new TestFetchJob(scenarios.first().requestUrl); QVERIFY(execJob(job)); QCOMPARE(static_cast(job->error()), scenarios.last().responseCode == 200 ? KGAPI2::NoError : scenarios.last().responseCode); QCOMPARE(job->response(), scenarios.last().responseData); QVERIFY(!FakeNetworkAccessManagerFactory::get()->hasScenario()); } void testAuthenticatedFetch_data() { QTest::addColumn>("scenarios"); QTest::newRow("success") << Scenarios{ - { QUrl(QStringLiteral("https://example.test/request/data")), QNetworkAccessManager::GetOperation, + { QUrl(QStringLiteral("https://example.test/request/data?prettyPrint=false")), QNetworkAccessManager::GetOperation, {}, 200, "Response" } }; QTest::newRow("token expired") << Scenarios{ - { QUrl(QStringLiteral("https://example.test/request/data")), QNetworkAccessManager::GetOperation, + { QUrl(QStringLiteral("https://example.test/request/data?prettyPrint=false")), QNetworkAccessManager::GetOperation, {}, KGAPI2::Unauthorized, {} } }; } void testAuthenticatedFetch() { QFETCH(QList, scenarios); FakeNetworkAccessManagerFactory::get()->setScenarios(scenarios); auto account = AccountPtr::create(QStringLiteral("MockAccount"), QStringLiteral("MockToken")); auto job = new TestFetchJob(account, scenarios.first().requestUrl); QVERIFY(execJob(job)); QCOMPARE(static_cast(job->error()), scenarios.last().responseCode == 200 ? KGAPI2::NoError : scenarios.last().responseCode); QCOMPARE(job->response(), scenarios.last().responseData); QVERIFY(!FakeNetworkAccessManagerFactory::get()->hasScenario()); } }; QTEST_GUILESS_MAIN(FetchJobTest) #include "fetchjobtest.moc" diff --git a/autotests/drive/data/about_fetch_request.txt b/autotests/drive/data/about_fetch_request.txt index be9ba21..6ae51a4 100644 --- a/autotests/drive/data/about_fetch_request.txt +++ b/autotests/drive/data/about_fetch_request.txt @@ -1 +1 @@ -GET https://www.googleapis.com/drive/v2/about?includeSubscribed=true +GET https://www.googleapis.com/drive/v2/about?includeSubscribed=true&prettyPrint=false diff --git a/autotests/drive/data/change1_fetch_request.txt b/autotests/drive/data/change1_fetch_request.txt index 008a8de..cf412db 100644 --- a/autotests/drive/data/change1_fetch_request.txt +++ b/autotests/drive/data/change1_fetch_request.txt @@ -1 +1 @@ -GET https://www.googleapis.com/drive/v2/changes/194?supportsAllDrives=true +GET https://www.googleapis.com/drive/v2/changes/194?supportsAllDrives=true&prettyPrint=false diff --git a/autotests/drive/data/changes_fetch_page1_request.txt b/autotests/drive/data/changes_fetch_page1_request.txt index 3721b1f..b78b819 100644 --- a/autotests/drive/data/changes_fetch_page1_request.txt +++ b/autotests/drive/data/changes_fetch_page1_request.txt @@ -1 +1 @@ -GET https://www.googleapis.com/drive/v2/changes?includeDeleted=true&includeSubscribed=true&includeItemsFromAllDrives=true&supportsAllDrives=true +GET https://www.googleapis.com/drive/v2/changes?includeDeleted=true&includeSubscribed=true&includeItemsFromAllDrives=true&supportsAllDrives=true&prettyPrint=false diff --git a/autotests/drive/data/changes_fetch_page2_request.txt b/autotests/drive/data/changes_fetch_page2_request.txt index 3a08221..5b89dd6 100644 --- a/autotests/drive/data/changes_fetch_page2_request.txt +++ b/autotests/drive/data/changes_fetch_page2_request.txt @@ -1 +1 @@ -GET https://www.googleapis.com/drive/v2/changes?includeDeleted=true&includeSubscribed=true&pageToken=195 +GET https://www.googleapis.com/drive/v2/changes?includeDeleted=true&includeSubscribed=true&pageToken=195&prettyPrint=false diff --git a/autotests/drive/data/file1_copy_request.txt b/autotests/drive/data/file1_copy_request.txt index e60fe06..e92a647 100644 --- a/autotests/drive/data/file1_copy_request.txt +++ b/autotests/drive/data/file1_copy_request.txt @@ -1,40 +1,40 @@ -POST https://www.googleapis.com/drive/v2/files/abcdefghijklmnopqrstuvwxyz/copy?convert=false&ocr=false&pinned=false +POST https://www.googleapis.com/drive/v2/files/abcdefghijklmnopqrstuvwxyz/copy?convert=false&ocr=false&pinned=false&prettyPrint=false Content-Type: application/json { "mimeType": "application/vnd.google-apps.spreadsheet", "thumbnailLink": "https://docs.google.com/feeds/vt?gd=true&id=someid&v=480&s=otherid&sz=s220", "labels": { "restricted": false, "starred": false, "viewed": false, "hidden": false, "trashed": false }, "etag": "\"bX7M5zOlGcEthti1qPHKQWp6SJA/MTUyMzM1MTc5MDUyNA\"", "lastModifyingUserName": "John Doe", "writersCanShare": true, "sharedWithMeDate": "2018-03-14T13:35:25Z", "title": "Super mega secret KDE PIM plans for world domination", "ownerNames": [ "Konqui Dev" ], "id": "abcdefghijklmnopqrstuvwxyz", "parents": [ { "id": "completelynewparentreference", "selfLink": "https://www.googleapis.com/drive/v2/files/abcdefghijklmnopqrstuvwxyz/parents/completelynewparentreference", "parentLink": "https://www.googleapis.com/drive/v2/files/completelynewparentreference" } ], "shared": true, "editable": false, "kind": "drive#file", "modifiedDate": "2018-04-10T09:16:30Z", "createdDate": "2018-01-05T11:35:58Z", "iconLink": "https://drive-thirdparty.googleusercontent.com/16/type/application/vnd.google-apps.spreadsheet", "embedLink": "https://docs.google.com/spreadsheets/d/abcdefghijklmnopqrstuvwxyz/htmlembed?ouid=116901758143213967333", "alternateLink": "https://docs.google.com/spreadsheets/d/abcdefghijklmnopqrstuvwxyz/edit?usp=drivesdk", "selfLink": "https://www.googleapis.com/drive/v2/files/abcdefghijklmnopqrstuvwxyz" } diff --git a/autotests/drive/data/file1_create_request.txt b/autotests/drive/data/file1_create_request.txt index cdfdbf2..b2fe5d8 100644 --- a/autotests/drive/data/file1_create_request.txt +++ b/autotests/drive/data/file1_create_request.txt @@ -1,40 +1,40 @@ -POST https://www.googleapis.com/drive/v2/files?convert=false&ocr=false&pinned=false&useContentAsIndexableText=false&supportsAllDrives=true +POST https://www.googleapis.com/drive/v2/files?convert=false&ocr=false&pinned=false&useContentAsIndexableText=false&supportsAllDrives=true&prettyPrint=false Content-Type: application/json { "mimeType": "application/vnd.google-apps.spreadsheet", "thumbnailLink": "https://docs.google.com/feeds/vt?gd=true&id=someid&v=480&s=otherid&sz=s220", "labels": { "restricted": false, "starred": false, "viewed": false, "hidden": false, "trashed": false }, "etag": "\"bX7M5zOlGcEthti1qPHKQWp6SJA/MTUyMzM1MTc5MDUyNA\"", "lastModifyingUserName": "John Doe", "writersCanShare": true, "sharedWithMeDate": "2018-03-14T13:35:25Z", "title": "Super mega secret KDE PIM plans for world domination", "ownerNames": [ "Konqui Dev" ], "id": "abcdefghijklmnopqrstuvwxyz", "parents": [ { "id": "zyxwvutsrqponmlkjihgfedcba", "selfLink": "https://www.googleapis.com/drive/v2/files/abcdefghijklmnopqrstuvwxyz/parents/zyxwvutsrqponmlkjihgfedcba", "parentLink": "https://www.googleapis.com/drive/v2/files/zyxwvutsrqponmlkjihgfedcba" } ], "shared": true, "editable": false, "kind": "drive#file", "modifiedDate": "2018-04-10T09:16:30Z", "createdDate": "2018-01-05T11:35:58Z", "iconLink": "https://drive-thirdparty.googleusercontent.com/16/type/application/vnd.google-apps.spreadsheet", "embedLink": "https://docs.google.com/spreadsheets/d/abcdefghijklmnopqrstuvwxyz/htmlembed?ouid=116901758143213967333", "alternateLink": "https://docs.google.com/spreadsheets/d/abcdefghijklmnopqrstuvwxyz/edit?usp=drivesdk", "selfLink": "https://www.googleapis.com/drive/v2/files/abcdefghijklmnopqrstuvwxyz" } diff --git a/autotests/drive/data/file2_copy_request.txt b/autotests/drive/data/file2_copy_request.txt index 9419708..5f52016 100644 --- a/autotests/drive/data/file2_copy_request.txt +++ b/autotests/drive/data/file2_copy_request.txt @@ -1,43 +1,43 @@ -POST https://www.googleapis.com/drive/v2/files/abc123def456ghi789/copy?convert=false&ocr=false&pinned=false +POST https://www.googleapis.com/drive/v2/files/abc123def456ghi789/copy?convert=false&ocr=false&pinned=false&prettyPrint=false Content-Type: application/json { "mimeType": "image/jpeg", "thumbnailLink": "https://docs.google.com/feeds/vt?gd=true&id=someid&v=480&s=otherid&sz=s220", "labels": { "restricted": false, "starred": false, "viewed": false, "hidden": false, "trashed": false }, "etag": "\"bX7M5zOlGcEthti1qPHKQWp6SJA/MTUxODk3NjU5MzAyMA\"", "lastModifyingUserName": "Konqui Dev", "md5Checksum": "dc74e95b97efce962ac3f31a0cdecccb", "writersCanShare": true, "fileExtension": "JPG", "title": "DSC_1287.JPG", "ownerNames": [ "Konqui Dev" ], "fileSize": 5106501, "id": "abc123def456ghi789", "parents": [ { "id": "someparentfolderid", "selfLink": "https://www.googleapis.com/drive/v2/files/abc123def456ghi789/parents/someparentfolderid", "parentLink": "https://www.googleapis.com/drive/v2/files/someparentfolderid" } ], "shared": true, "kind": "drive#file", "modifiedDate": "2018-02-18T17:56:33Z", "createdDate": "2018-02-18T17:56:33Z", "thumbnailLink": "https://lh3.googleusercontent.com/abc123def456ghi789=s220", "iconLink": "https://drive-thirdparty.googleusercontent.com/16/type/image/jpeg", "embedLink": "https://drive.google.com/file/d/abc123def456ghi789/preview?usp=drivesdk", "alternateLink": "https://drive.google.com/file/d/abc123def456ghi789/view?usp=drivesdk", "selfLink": "https://www.googleapis.com/drive/v2/files/abc123def456ghi789", "downloadUrl": "https://doc-0o-5g-docs.googleusercontent.com/docs/securesc/abc123def456ghi789?e=download&gd=true", "webContentLink": "https://drive.google.com/uc?id=abc123def456ghi789&export=download" } diff --git a/autotests/drive/data/file2_create_request.txt b/autotests/drive/data/file2_create_request.txt index 5574ece..ef1d98e 100644 Binary files a/autotests/drive/data/file2_create_request.txt and b/autotests/drive/data/file2_create_request.txt differ diff --git a/autotests/drive/data/teamdrive_create_request.txt b/autotests/drive/data/teamdrive_create_request.txt index d6f1eb5..a991303 100644 --- a/autotests/drive/data/teamdrive_create_request.txt +++ b/autotests/drive/data/teamdrive_create_request.txt @@ -1,7 +1,7 @@ -POST https://www.googleapis.com/drive/v2/teamdrives?requestId=MockRequestId +POST https://www.googleapis.com/drive/v2/teamdrives?requestId=MockRequestId&prettyPrint=false { "kind": "drive#teamDrive", "id": "0APqEW6eViiMXUk9PVA", "name": "First Team Drive" } diff --git a/autotests/drive/data/teamdrive_fetch_request.txt b/autotests/drive/data/teamdrive_fetch_request.txt index e2b117d..d12bd99 100644 --- a/autotests/drive/data/teamdrive_fetch_request.txt +++ b/autotests/drive/data/teamdrive_fetch_request.txt @@ -1 +1 @@ -GET https://www.googleapis.com/drive/v2/teamdrives +GET https://www.googleapis.com/drive/v2/teamdrives?prettyPrint=false diff --git a/autotests/drive/data/teamdrive_modify_request.txt b/autotests/drive/data/teamdrive_modify_request.txt index 33dd1c8..f930cc5 100644 --- a/autotests/drive/data/teamdrive_modify_request.txt +++ b/autotests/drive/data/teamdrive_modify_request.txt @@ -1,7 +1,7 @@ -POST https://www.googleapis.com/drive/v2/teamdrives/0APqEW6eViiMXUk9PVA +POST https://www.googleapis.com/drive/v2/teamdrives/0APqEW6eViiMXUk9PVA?prettyPrint=false { "kind": "drive#teamDrive", "id": "0APqEW6eViiMXUk9PVA", "name": "Renamed Team Drive" } diff --git a/autotests/tasks/data/task1_create_request.txt b/autotests/tasks/data/task1_create_request.txt index 9792b2c..70ff3b3 100644 --- a/autotests/tasks/data/task1_create_request.txt +++ b/autotests/tasks/data/task1_create_request.txt @@ -1,11 +1,11 @@ -POST https://www.googleapis.com/tasks/v1/lists/MockAccount/tasks +POST https://www.googleapis.com/tasks/v1/lists/MockAccount/tasks?prettyPrint=false Content-Type: application/json { "completed": "2016-03-22T19:52:34.000Z", "due": "2016-03-23T00:00:00.000Z", "kind": "tasks#task", "notes": "This task has been completed", "status": "completed", "title": "Test Task" } diff --git a/autotests/tasks/data/task1_fetch_request.txt b/autotests/tasks/data/task1_fetch_request.txt index 3edee93..725bd2c 100644 --- a/autotests/tasks/data/task1_fetch_request.txt +++ b/autotests/tasks/data/task1_fetch_request.txt @@ -1 +1 @@ -GET https://www.googleapis.com/tasks/v1/lists/MockAccount/tasks/MDk5OTU2NTg1OTM5NjgzODgzMDk6MDoxOTU0MzA5MzY1 +GET https://www.googleapis.com/tasks/v1/lists/MockAccount/tasks/MDk5OTU2NTg1OTM5NjgzODgzMDk6MDoxOTU0MzA5MzY1?prettyPrint=false diff --git a/autotests/tasks/data/task1_modify_request.txt b/autotests/tasks/data/task1_modify_request.txt index e05b585..b84651e 100644 --- a/autotests/tasks/data/task1_modify_request.txt +++ b/autotests/tasks/data/task1_modify_request.txt @@ -1,13 +1,13 @@ -PUT https://www.googleapis.com/tasks/v1/lists/MockAccount/tasks/MDk5OTU2NTg1OTM5NjgzODgzMDk6MDoxOTU0MzA5MzY1 +PUT https://www.googleapis.com/tasks/v1/lists/MockAccount/tasks/MDk5OTU2NTg1OTM5NjgzODgzMDk6MDoxOTU0MzA5MzY1?prettyPrint=false Content-Type: application/json { "kind": "tasks#task", "title": "Test Task", "status": "completed", "notes": "This task has been completed", "completed": "2016-03-22T19:52:34.000Z", "due": "2016-03-23T00:00:00.000Z", "id": "MDk5OTU2NTg1OTM5NjgzODgzMDk6MDoxOTU0MzA5MzY1" } diff --git a/autotests/tasks/data/task1_move_request.txt b/autotests/tasks/data/task1_move_request.txt index dd55c8b..34bfb27 100644 --- a/autotests/tasks/data/task1_move_request.txt +++ b/autotests/tasks/data/task1_move_request.txt @@ -1 +1 @@ -POST https://www.googleapis.com/tasks/v1/lists/MockAccount/tasks/MDk5OTU2NTg1OTM5NjgzODgzMDk6MDoxOTU0MzA5MzY1/move +POST https://www.googleapis.com/tasks/v1/lists/MockAccount/tasks/MDk5OTU2NTg1OTM5NjgzODgzMDk6MDoxOTU0MzA5MzY1/move?prettyPrint=false diff --git a/autotests/tasks/data/task2_create_request.txt b/autotests/tasks/data/task2_create_request.txt index 79dbde7..20f0cb1 100644 --- a/autotests/tasks/data/task2_create_request.txt +++ b/autotests/tasks/data/task2_create_request.txt @@ -1,10 +1,10 @@ -POST https://www.googleapis.com/tasks/v1/lists/MockAccount/tasks +POST https://www.googleapis.com/tasks/v1/lists/MockAccount/tasks?prettyPrint=false Content-Type: application/json { "due": "2017-01-23T00:00:00.000Z", "title": "A Task", "kind": "tasks#task", "notes": "", "status": "needsAction" } diff --git a/autotests/tasks/data/task2_modify_request.txt b/autotests/tasks/data/task2_modify_request.txt index 7c85f44..29fcdbe 100644 --- a/autotests/tasks/data/task2_modify_request.txt +++ b/autotests/tasks/data/task2_modify_request.txt @@ -1,11 +1,11 @@ -PUT https://www.googleapis.com/tasks/v1/lists/MockAccount/tasks/MDk5OTU2NTg1OTM5NjgzODgzMDk6MDoxNTczOTM0NTg3 +PUT https://www.googleapis.com/tasks/v1/lists/MockAccount/tasks/MDk5OTU2NTg1OTM5NjgzODgzMDk6MDoxNTczOTM0NTg3?prettyPrint=false Content-Type: application/json { "status": "needsAction", "kind": "tasks#task", "title": "A Task", "notes": "", "due": "2017-01-23T00:00:00.000Z", "id": "MDk5OTU2NTg1OTM5NjgzODgzMDk6MDoxNTczOTM0NTg3" } diff --git a/autotests/tasks/data/task2_move_request.txt b/autotests/tasks/data/task2_move_request.txt index a001045..25ee729 100644 --- a/autotests/tasks/data/task2_move_request.txt +++ b/autotests/tasks/data/task2_move_request.txt @@ -1 +1 @@ -POST https://www.googleapis.com/tasks/v1/lists/MockAccount/tasks/MDk5OTU2NTg1OTM5NjgzODgzMDk6MDoxNTczOTM0NTg3/move +POST https://www.googleapis.com/tasks/v1/lists/MockAccount/tasks/MDk5OTU2NTg1OTM5NjgzODgzMDk6MDoxNTczOTM0NTg3/move?prettyPrint=false diff --git a/autotests/tasks/data/task2_move_with_parent_request.txt b/autotests/tasks/data/task2_move_with_parent_request.txt index 4cfd66a..828a313 100644 --- a/autotests/tasks/data/task2_move_with_parent_request.txt +++ b/autotests/tasks/data/task2_move_with_parent_request.txt @@ -1 +1 @@ -POST https://www.googleapis.com/tasks/v1/lists/MockAccount/tasks/MDk5OTU2NTg1OTM5NjgzODgzMDk6MDoxNTczOTM0NTg3/move?parent=newMockParent +POST https://www.googleapis.com/tasks/v1/lists/MockAccount/tasks/MDk5OTU2NTg1OTM5NjgzODgzMDk6MDoxNTczOTM0NTg3/move?parent=newMockParent&prettyPrint=false diff --git a/autotests/tasks/data/tasklist1_create_request.txt b/autotests/tasks/data/tasklist1_create_request.txt index a645ff5..ddcb95c 100644 --- a/autotests/tasks/data/tasklist1_create_request.txt +++ b/autotests/tasks/data/tasklist1_create_request.txt @@ -1,8 +1,8 @@ -POST https://www.googleapis.com/tasks/v1/users/@me/lists +POST https://www.googleapis.com/tasks/v1/users/@me/lists?prettyPrint=false Content-type: application/json { "kind": "tasks#taskList", "title": "Todo" } diff --git a/autotests/tasks/data/tasklist1_modify_request.txt b/autotests/tasks/data/tasklist1_modify_request.txt index db5a6ff..661445c 100644 --- a/autotests/tasks/data/tasklist1_modify_request.txt +++ b/autotests/tasks/data/tasklist1_modify_request.txt @@ -1,10 +1,10 @@ -PUT https://www.googleapis.com/tasks/v1/users/@me/lists/MDk5OTU2NTg1OTM5NjgzODgzMDk6MDow +PUT https://www.googleapis.com/tasks/v1/users/@me/lists/MDk5OTU2NTg1OTM5NjgzODgzMDk6MDow?prettyPrint=false Content-type: application/json { "id": "MDk5OTU2NTg1OTM5NjgzODgzMDk6MDow", "kind": "tasks#taskList", "title": "Todo" } diff --git a/autotests/tasks/data/tasklist2_create_request.txt b/autotests/tasks/data/tasklist2_create_request.txt index 68dd3f4..1d6eae5 100644 --- a/autotests/tasks/data/tasklist2_create_request.txt +++ b/autotests/tasks/data/tasklist2_create_request.txt @@ -1,7 +1,7 @@ -POST https://www.googleapis.com/tasks/v1/users/@me/lists +POST https://www.googleapis.com/tasks/v1/users/@me/lists?prettyPrint=false Content-type: application/json { "kind": "tasks#taskList", "title": "Work" } diff --git a/autotests/tasks/data/tasklist2_modify_request.txt b/autotests/tasks/data/tasklist2_modify_request.txt index aaf3fc7..e1bec3f 100644 --- a/autotests/tasks/data/tasklist2_modify_request.txt +++ b/autotests/tasks/data/tasklist2_modify_request.txt @@ -1,9 +1,9 @@ -PUT https://www.googleapis.com/tasks/v1/users/@me/lists/MDk5OTU2NTg1OTM5NjgzODgzMDk6MzU1NDQ5NDI4ODIzODI5ODow +PUT https://www.googleapis.com/tasks/v1/users/@me/lists/MDk5OTU2NTg1OTM5NjgzODgzMDk6MzU1NDQ5NDI4ODIzODI5ODow?prettyPrint=false Content-type: application/json { "id": "MDk5OTU2NTg1OTM5NjgzODgzMDk6MzU1NDQ5NDI4ODIzODI5ODow", "kind": "tasks#taskList", "title": "Work" } diff --git a/autotests/tasks/data/tasklists_fetch_page1_request.txt b/autotests/tasks/data/tasklists_fetch_page1_request.txt index f914f2e..dd0b146 100644 --- a/autotests/tasks/data/tasklists_fetch_page1_request.txt +++ b/autotests/tasks/data/tasklists_fetch_page1_request.txt @@ -1 +1 @@ -GET https://www.googleapis.com/tasks/v1/users/@me/lists +GET https://www.googleapis.com/tasks/v1/users/@me/lists?prettyPrint=false diff --git a/autotests/tasks/data/tasklists_fetch_page2_request.txt b/autotests/tasks/data/tasklists_fetch_page2_request.txt index 2d0fd00..5a133c1 100644 --- a/autotests/tasks/data/tasklists_fetch_page2_request.txt +++ b/autotests/tasks/data/tasklists_fetch_page2_request.txt @@ -1 +1 @@ -GET https://www.googleapis.com/tasks/v1/users/@me/lists?pageToken=MDk5OTU2NTg1OTM5NjgzODgzMDk6NjExNzc0NDYwNjE5ODE5MTow&maxResults=20 +GET https://www.googleapis.com/tasks/v1/users/@me/lists?pageToken=MDk5OTU2NTg1OTM5NjgzODgzMDk6NjExNzc0NDYwNjE5ODE5MTow&maxResults=20&prettyPrint=false diff --git a/autotests/tasks/data/tasks_fetch_page1_request.txt b/autotests/tasks/data/tasks_fetch_page1_request.txt index 3f55abe..d58c7a1 100644 --- a/autotests/tasks/data/tasks_fetch_page1_request.txt +++ b/autotests/tasks/data/tasks_fetch_page1_request.txt @@ -1 +1 @@ -GET https://www.googleapis.com/tasks/v1/lists/MockAccount/tasks?showDeleted=true&showCompleted=true +GET https://www.googleapis.com/tasks/v1/lists/MockAccount/tasks?showDeleted=true&showCompleted=true&prettyPrint=false diff --git a/autotests/tasks/data/tasks_fetch_page2_request.txt b/autotests/tasks/data/tasks_fetch_page2_request.txt index b4bb9c5..54c8fbf 100644 --- a/autotests/tasks/data/tasks_fetch_page2_request.txt +++ b/autotests/tasks/data/tasks_fetch_page2_request.txt @@ -1,2 +1,2 @@ -GET https://www.googleapis.com/tasks/v1/lists/MockAccount/tasks?pageToken=MDk5OTU2NTg1OTM5NjgzODgzMDk6MDoxOTU0MzA5MzY1&maxResults=20 +GET https://www.googleapis.com/tasks/v1/lists/MockAccount/tasks?pageToken=MDk5OTU2NTg1OTM5NjgzODgzMDk6MDoxOTU0MzA5MzY1&maxResults=20&prettyPrint=false diff --git a/src/contacts/contactmodifyjob.cpp b/src/contacts/contactmodifyjob.cpp index 2f74adc..dac85ae 100644 --- a/src/contacts/contactmodifyjob.cpp +++ b/src/contacts/contactmodifyjob.cpp @@ -1,176 +1,184 @@ /* * This file is part of LibKGAPI library * * Copyright (C) 2013 Daniel Vrátil * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #include "contactmodifyjob.h" #include "contactsservice.h" #include "contact.h" #include "../debug.h" #include "utils.h" #include "account.h" #include "private/queuehelper_p.h" +#include #include #include #include #include using namespace KGAPI2; class Q_DECL_HIDDEN ContactModifyJob::Private { public: Private(ContactModifyJob *parent); void processNextContact(); QueueHelper contacts; ContactPtr lastContact; QPair pendingPhoto; private: ContactModifyJob *q; }; ContactModifyJob::Private::Private(ContactModifyJob *parent): q(parent) { } void ContactModifyJob::Private::processNextContact() { if (contacts.atEnd()) { if (pendingPhoto.first.isEmpty()) { q->emitFinished(); } return; } const ContactPtr contact = contacts.current(); const QUrl url = ContactsService::updateContactUrl(q->account()->accountName(), contact->uid()); QNetworkRequest request(url); QByteArray rawData = ContactsService::contactToXML(contact); rawData.prepend("" ""); rawData.append(""); QStringList headers; auto rawHeaderList = request.rawHeaderList(); headers.reserve(rawHeaderList.size()); for (const QByteArray &str : qAsConst(rawHeaderList)) { headers << QLatin1String(str) + QLatin1String(": ") + QLatin1String(request.rawHeader(str)); } q->enqueueRequest(request, rawData, QStringLiteral("application/atom+xml")); const QUrl photoUrl = ContactsService::photoUrl(q->account()->accountName(), contact->uid()); QNetworkRequest photoRequest(photoUrl); if (!contact->photo().isEmpty()) { photoRequest.setHeader(QNetworkRequest::ContentTypeHeader, QLatin1String("image/*")); pendingPhoto.first = contact->photo().rawData(); pendingPhoto.second = contact->photo().type(); q->enqueueRequest(photoRequest, pendingPhoto.first, QStringLiteral("modifyImage")); } else { q->enqueueRequest(photoRequest, QByteArray(), QStringLiteral("deleteImage")); } } ContactModifyJob::ContactModifyJob(const ContactsList& contacts, const AccountPtr& account, QObject* parent): ModifyJob(account, parent), d(new Private(this)) { d->contacts = contacts; } ContactModifyJob::ContactModifyJob(const ContactPtr& contact, const AccountPtr& account, QObject* parent): ModifyJob(account, parent), d(new Private(this)) { d->contacts << contact; } ContactModifyJob::~ContactModifyJob() { delete d; } void ContactModifyJob::start() { d->processNextContact(); } void ContactModifyJob::dispatchRequest(QNetworkAccessManager *accessManager, const QNetworkRequest &request, const QByteArray &data, const QString &contentType) { QNetworkRequest r = request; r.setRawHeader("If-Match", "*"); r.setRawHeader("GData-Version", ContactsService::APIVersion().toLatin1()); if (contentType == QLatin1String("modifyImage")) { accessManager->put(r, data); } else if (contentType == QLatin1String("deleteImage")) { + // Delete requests have no response body so there isn't anything to pretty print + QUrl cleanedUrl = r.url(); + QUrlQuery cleanedQuery(cleanedUrl); + cleanedQuery.removeAllQueryItems(Job::StandardParams::PrettyPrint); + cleanedUrl.setQuery(cleanedQuery); + r.setUrl(cleanedUrl); + accessManager->deleteResource(r); } else { r.setHeader(QNetworkRequest::ContentTypeHeader, contentType); accessManager->put(r, data); } } ObjectsList ContactModifyJob::handleReplyWithItems(const QNetworkReply *reply, const QByteArray& rawData) { ObjectsList items; if (!reply->url().path().contains(QLatin1String("/photos/media/"))) { const QString contentType = reply->header(QNetworkRequest::ContentTypeHeader).toString(); ContentType ct = Utils::stringToContentType(contentType); if (ct == KGAPI2::JSON) { d->lastContact = ContactsService::JSONToContact(rawData); items << d->lastContact; d->contacts.currentProcessed(); } else if (ct == KGAPI2::XML) { d->lastContact = ContactsService::XMLToContact(rawData); items << d->lastContact; d->contacts.currentProcessed(); } else { setError(KGAPI2::InvalidResponse); setErrorString(tr("Invalid response content type")); emitFinished(); return items; } } else { if (d->lastContact && !d->pendingPhoto.first.isEmpty()) { KContacts::Picture picture; picture.setRawData(d->pendingPhoto.first, d->pendingPhoto.second); d->lastContact->setPhoto(picture); d->pendingPhoto.first.clear(); d->pendingPhoto.second.clear(); } // Enqueue next item and picture or finish d->processNextContact(); } return items; } diff --git a/src/core/deletejob.cpp b/src/core/deletejob.cpp index 0f598ff..e2307bd 100644 --- a/src/core/deletejob.cpp +++ b/src/core/deletejob.cpp @@ -1,71 +1,79 @@ /* * This file is part of LibKGAPI library * * Copyright (C) 2013 Daniel Vrátil * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #include "deletejob.h" +#include #include #include using namespace KGAPI2; class Q_DECL_HIDDEN DeleteJob::Private { }; DeleteJob::DeleteJob(QObject* parent): Job(parent), d(new Private) { } DeleteJob::DeleteJob(const AccountPtr& account, QObject* parent): Job(account, parent), d(new Private) { } DeleteJob::~DeleteJob() { delete d; } void DeleteJob::dispatchRequest(QNetworkAccessManager* accessManager, const QNetworkRequest& request, const QByteArray& data, const QString& contentType) { Q_UNUSED(data) Q_UNUSED(contentType) QNetworkRequest r = request; if (!r.hasRawHeader("If-Match")) { r.setRawHeader("If-Match", "*"); } + // Delete requests have no response body so there isn't anything to pretty print + QUrl cleanedUrl = r.url(); + QUrlQuery cleanedQuery(cleanedUrl); + cleanedQuery.removeAllQueryItems(Job::StandardParams::PrettyPrint); + cleanedUrl.setQuery(cleanedQuery); + r.setUrl(cleanedUrl); + accessManager->deleteResource(r); } void DeleteJob::handleReply(const QNetworkReply *reply, const QByteArray& rawData) { Q_UNUSED(reply) Q_UNUSED(rawData) start(); } diff --git a/src/core/job.cpp b/src/core/job.cpp index 3fb37e6..50b78b1 100644 --- a/src/core/job.cpp +++ b/src/core/job.cpp @@ -1,492 +1,521 @@ /* * This file is part of LibKGAPI library * * Copyright (C) 2013 Daniel Vrátil * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #include "job.h" #include "job_p.h" #include "account.h" #include "networkaccessmanagerfactory_p.h" #include "../debug.h" #include "authjob.h" #include #include #include #include +#include using namespace KGAPI2; FileLogger *FileLogger::sInstance = nullptr; FileLogger::FileLogger() { if (!qEnvironmentVariableIsSet("KGAPI_SESSION_LOGFILE")) { return; } QString filename = QString::fromLocal8Bit(qgetenv("KGAPI_SESSION_LOGFILE")) + QLatin1Char('.') + QString::number(QCoreApplication::applicationPid()); mFile.reset(new QFile(filename)); if (!mFile->open(QIODevice::WriteOnly | QIODevice::Truncate)) { qCWarning(KGAPIDebug) << "Failed to open logging file" << filename << ":" << mFile->errorString(); mFile.reset(); } } FileLogger::~FileLogger() {} FileLogger *FileLogger::self() { if (!sInstance) { sInstance = new FileLogger(); } return sInstance; } void FileLogger::logRequest(const QNetworkRequest &request, const QByteArray &rawData) { if (!mFile) { return; } QTextStream stream(mFile.data()); stream << "C: " << request.url().toDisplayString() << "\n"; const auto headers = request.rawHeaderList(); for (const auto &header : headers) { stream << " " << header << ": " << request.rawHeader(header) << "\n"; } stream << " " << rawData << "\n\n"; mFile->flush(); } void FileLogger::logReply(const QNetworkReply *reply, const QByteArray &rawData) { if (!mFile) { return; } QTextStream stream(mFile.data()); stream << "S: " << reply->url().toDisplayString() << "\n"; const auto headers = reply->rawHeaderList(); for (const auto &header : headers) { stream << " " << header << ": " << reply->rawHeader(header) << "\n"; } stream << " " << rawData << "\n\n"; mFile->flush(); } Job::Private::Private(Job *parent): isRunning(false), error(KGAPI2::NoError), accessManager(nullptr), maxTimeout(0), + prettyPrint(false), q(parent) { } void Job::Private::init() { QTimer::singleShot(0, q, [this]() { _k_doStart(); }); accessManager = NetworkAccessManagerFactory::instance()->networkAccessManager(q); connect(accessManager, &QNetworkAccessManager::finished, q, [this](QNetworkReply *reply) { _k_replyReceived(reply); }); dispatchTimer = new QTimer(q); connect(dispatchTimer, &QTimer::timeout, q, [this]() { _k_dispatchTimeout(); }); } QString Job::Private::parseErrorMessage(const QByteArray &json) { QJsonDocument document = QJsonDocument::fromJson(json); if (!document.isNull()) { QVariantMap map = document.toVariant().toMap(); QString message; if (map.contains(QStringLiteral("error"))) { map = map.value(QStringLiteral("error")).toMap(); } if (map.contains(QStringLiteral("message"))) { message.append(map.value(QStringLiteral("message")).toString()); } else { message = QLatin1String(json); } return message; } else { return QLatin1String(json); } } void Job::Private::_k_doStart() { isRunning = true; q->aboutToStart(); q->start(); } void Job::Private::_k_doEmitFinished() { Q_EMIT q->finished(q); } void Job::Private::_k_replyReceived(QNetworkReply* reply) { int replyCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); if (replyCode == 0) { /* Workaround for a bug (??), when QNetworkReply does not report HTTP/1.1 401 Unauthorized * as an error. */ if (!reply->rawHeaderList().isEmpty()) { QString status = QLatin1String(reply->rawHeaderList().first()); if (status.startsWith(QLatin1String("HTTP/1.1 401"))) replyCode = KGAPI2::Unauthorized; } } const QByteArray rawData = reply->readAll(); qCDebug(KGAPIDebug) << "Received reply from" << reply->url(); qCDebug(KGAPIDebug) << "Status code: " << replyCode; FileLogger::self()->logReply(reply, rawData); switch (replyCode) { case KGAPI2::NoError: case KGAPI2::OK: /** << OK status (fetched, updated, removed) */ case KGAPI2::Created: /** << OK status (created) */ case KGAPI2::NoContent: /** << OK status (removed task using Tasks API) */ break; case KGAPI2::TemporarilyMoved: { /** << Temporarily moved - Google provides a new URL where to send the request */ qCDebug(KGAPIDebug) << "Google says: Temporarily moved to " << reply->header(QNetworkRequest::LocationHeader).toUrl(); QNetworkRequest request = currentRequest.request; request.setUrl(reply->header(QNetworkRequest::LocationHeader).toUrl()); q->enqueueRequest(request, currentRequest.rawData, currentRequest.contentType); return; } case KGAPI2::BadRequest: /** << Bad request - malformed data, API changed, something went wrong... */ qCWarning(KGAPIDebug) << "Bad request, Google replied '" << rawData << "'"; q->setError(KGAPI2::BadRequest); q->setErrorString(tr("Bad request.")); q->emitFinished(); return; case KGAPI2::Unauthorized: /** << Unauthorized - Access token has expired, request a new token */ qCWarning(KGAPIDebug) << "Unauthorized. Access token has expired or is invalid."; q->setError(KGAPI2::Unauthorized); q->setErrorString(tr("Invalid authentication.")); q->emitFinished(); return; case KGAPI2::Forbidden: { qCWarning(KGAPIDebug) << "Requested resource is forbidden."; const QString msg = parseErrorMessage(rawData); q->setError(KGAPI2::Forbidden); q->setErrorString(tr("Requested resource is forbidden.\n\nGoogle replied '%1'").arg(msg)); q->emitFinished(); return; } case KGAPI2::NotFound: { qCWarning(KGAPIDebug) << "Requested resource does not exist"; const QString msg = parseErrorMessage(rawData); q->setError(KGAPI2::NotFound); q->setErrorString(tr("Requested resource does not exist.\n\nGoogle replied '%1'").arg(msg)); // don't emit finished() here, we can get 404 when fetching contact photos or so, // in that case 404 is not fatal. Let subclass decide whether to terminate or not. q->handleReply(reply, rawData); if (requestQueue.isEmpty()) { q->emitFinished(); } return; } case KGAPI2::Conflict: { qCWarning(KGAPIDebug) << "Conflict. Remote resource is newer then local."; const QString msg = parseErrorMessage(rawData); q->setError(KGAPI2::Conflict); q->setErrorString(tr("Conflict. Remote resource is newer than local.\n\nGoogle replied '%1'").arg(msg)); q->emitFinished(); return; } case KGAPI2::Gone: { qCWarning(KGAPIDebug) << "Requested resource does not exist anymore."; const QString msg = parseErrorMessage(rawData); q->setError(KGAPI2::Gone); q->setErrorString(tr("Requested resource does not exist anymore.\n\nGoogle replied '%1'").arg(msg)); q->emitFinished(); return; } case KGAPI2::InternalError: { qCWarning(KGAPIDebug) << "Internal server error."; const QString msg = parseErrorMessage(rawData); q->setError(KGAPI2::InternalError); q->setErrorString(tr("Internal server error. Try again later.\n\nGoogle replied '%1'").arg(msg)); q->emitFinished(); return; } case KGAPI2::QuotaExceeded: { qCWarning(KGAPIDebug) << "User quota exceeded."; // Extend the interval (if possible) and enqueue the request again int interval = dispatchTimer->interval() / 1000; if (interval == 0) { interval = 1; } else if (interval == 1) { interval = 2; } else if ((interval > maxTimeout) && (maxTimeout > 0)) { const QString msg = parseErrorMessage(rawData); q->setError(KGAPI2::QuotaExceeded); q->setErrorString(tr("Maximum quota exceeded. Try again later.\n\nGoogle replied '%1'").arg(msg)); q->emitFinished(); return; } else { interval = interval ^ 2; } qCDebug(KGAPIDebug) << "Increasing dispatch interval to" << interval * 1000 << "msecs"; dispatchTimer->setInterval(interval * 1000); const QNetworkRequest request = reply->request(); q->enqueueRequest(request); if (!dispatchTimer->isActive()) { dispatchTimer->start(); } return; } default:{ /** Something went wrong, there's nothing we can do about it */ qCWarning(KGAPIDebug) << "Unknown error" << reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); const QString msg = parseErrorMessage(rawData); q->setError(KGAPI2::UnknownError); q->setErrorString(tr("Unknown error.\n\nGoogle replied '%1'").arg(msg)); q->emitFinished(); return; } } q->handleReply(reply, rawData); // handleReply has terminated the job, don't continue if (!q->isRunning()) { return; } qCDebug(KGAPIDebug) << requestQueue.length() << "requests in requestQueue."; if (requestQueue.isEmpty()) { q->emitFinished(); return; } if (!dispatchTimer->isActive()) { dispatchTimer->start(); } } void Job::Private::_k_dispatchTimeout() { if (requestQueue.isEmpty()) { dispatchTimer->stop(); return; } const Request r = requestQueue.dequeue(); currentRequest = r; QNetworkRequest authorizedRequest = r.request; if (account) { authorizedRequest.setRawHeader("Authorization", "Bearer " + account->accessToken().toLatin1()); } + + QUrl url = authorizedRequest.url(); + QUrlQuery standardParamQuery(url); + + if (!standardParamQuery.hasQueryItem(Job::StandardParams::PrettyPrint)) { + standardParamQuery.addQueryItem(Job::StandardParams::PrettyPrint, prettyPrint ? QStringLiteral("true") : QStringLiteral("false")); + } + + url.setQuery(standardParamQuery); + authorizedRequest.setUrl(url); + qCDebug(KGAPIDebug) << q << "Dispatching request to" << r.request.url(); FileLogger::self()->logRequest(authorizedRequest, r.rawData); q->dispatchRequest(accessManager, authorizedRequest, r.rawData, r.contentType); if (requestQueue.isEmpty()) { dispatchTimer->stop(); } } /************************* PUBLIC **********************/ +const QString Job::StandardParams::PrettyPrint = QStringLiteral("prettyPrint"); + Job::Job(QObject* parent): QObject(parent), d(new Private(this)) { d->init(); } Job::Job(const AccountPtr& account, QObject* parent): QObject(parent), d(new Private(this)) { d->account = account; d->init(); } Job::~Job() { delete d; } void Job::setError(Error error) { d->error = error; } Error Job::error() const { if (isRunning()) { qCWarning(KGAPIDebug) << "Called error() on running job, returning nothing"; return KGAPI2::NoError; } return d->error; } void Job::setErrorString(const QString& errorString) { d->errorString = errorString; } QString Job::errorString() const { if (isRunning()) { qCWarning(KGAPIDebug) << "Called errorString() on running job, returning nothing"; return QString(); } return d->errorString; } bool Job::isRunning() const { return d->isRunning; } int Job::maxTimeout() const { return d->maxTimeout; } void Job::setMaxTimeout(int maxTimeout) { if (isRunning()) { qCWarning(KGAPIDebug) << "Called setMaxTimeout() on running job. Ignoring."; return; } d->maxTimeout = maxTimeout; } AccountPtr Job::account() const { return d->account; } void Job::setAccount(const AccountPtr& account) { if (d->isRunning) { qCWarning(KGAPIDebug) << "Called setAccount() on running job. Ignoring."; return; } d->account = account; } +bool Job::prettyPrint() const +{ + return d->prettyPrint; +} + +void Job::setPrettyPrint(bool prettyPrint) +{ + if (d->isRunning) { + qCWarning(KGAPIDebug) << "Called setPrettyPrint() on running job. Ignoring."; + return; + } + + d->prettyPrint = prettyPrint; +} void Job::restart() { if (d->isRunning) { qCWarning(KGAPIDebug) << "Running job cannot be restarted."; return; } QTimer::singleShot(0, this, [this]() { d->_k_doStart();}); } void Job::emitFinished() { qCDebug(KGAPIDebug); aboutToFinish(); d->isRunning = false; d->dispatchTimer->stop(); d->requestQueue.clear(); // Emit in next event loop iteration so that the method caller can finish // before user is notified QTimer::singleShot(0, this, [this]() { d->_k_doEmitFinished(); }); } void Job::emitProgress(int processed, int total) { Q_EMIT progress(this, processed, total); } void Job::enqueueRequest(const QNetworkRequest& request, const QByteArray& data, const QString& contentType) { if (!isRunning()) { qCDebug(KGAPIDebug) << "Can't enqueue requests when job is not running."; qCDebug(KGAPIDebug) << "Not enqueueing" << request.url(); return; } qCDebug(KGAPIDebug) << "Queued" << request.url(); Request r_; r_.request = request; r_.rawData = data; r_.contentType = contentType; d->requestQueue.enqueue(r_); if (!d->dispatchTimer->isActive()) { d->dispatchTimer->start(); } } void Job::aboutToFinish() { } void Job::aboutToStart() { d->error = KGAPI2::NoError; d->errorString.clear(); d->currentRequest.contentType.clear(); d->currentRequest.rawData.clear(); d->currentRequest.request = QNetworkRequest(); d->dispatchTimer->setInterval(0); } #include "moc_job.cpp" diff --git a/src/core/job.h b/src/core/job.h index 9912f18..6de1408 100644 --- a/src/core/job.h +++ b/src/core/job.h @@ -1,336 +1,359 @@ /* * This file is part of LibKGAPI library * * Copyright (C) 2013 Daniel Vrátil * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef LIBKGAPI2_JOB_H #define LIBKGAPI2_JOB_H #include "types.h" #include "kgapicore_export.h" #include +#include class QNetworkAccessManager; class QNetworkReply; class QNetworkRequest; namespace KGAPI2 { /** * @headerfile job.h * @brief Abstract base class for all jobs in LibKGAPI * * Usual workflow of Job subclasses is to reimplement Job::start, * Job::dispatchRequest and Job::handleReply, then enqueue a QNetworkRequest using * Job::enqueueRequest. Authorization headers and standard query parameters will be * set by Job class. The request will automatically be scheduled in a queue and * dispatched by calling Job::dispatchRequest implementation. When a reply is received, * the Job will automatically perform error handling and if there is no error, the * reply is passed to implementation of Job::handleReply. * * Job is automatically started when program enters an event loop. * * @author Daniel Vrátil * @since 2.0 */ class KGAPICORE_EXPORT Job : public QObject { Q_OBJECT /** * @brief Maximum interval between requests. * * Some Google APIs have a quota on maximum amount of requests per account * per second. When this quota is exceeded, the Job will automatically increase * the interval between dispatching requests, wait for a while and then try * again. If however the interval is increased over @p maxTimeout, the job * will fail and finish immediately. By default @p maxTimeout is @p -1, which * allows the interval to be increased indefinitely. * * @see Job::maxTimeout, Job::setMaxTimeout */ Q_PROPERTY(int maxTimeout READ maxTimeout WRITE setMaxTimeout) /** * @brief Whether the job is running * * This property indicates whether the job is running or not. The value is * set to @p true when the job is started (see Job::start) and back to * @p false right before Job::finished is emitted. * * @see Job::isRunning, Job::finished */ Q_PROPERTY(bool isRunning READ isRunning NOTIFY finished) public: /** * @brief Constructor for jobs that don't require authentication * * @param parent */ explicit Job(QObject* parent = nullptr); /** * @brief Constructor for jobs that require authentication * * @param account Account to use to authenticate the requests send by this job * @param parent * @see Job::Account, Job::setAccount */ explicit Job(const AccountPtr &account, QObject* parent = nullptr); + struct StandardParams { + static const QString PrettyPrint; + }; + /** * @brief Destructor */ ~Job() override; /** * @brief Error code * * This method can only be called after the job has emitted Job::finished * signal. Calling this method on a running job will always return * KGAPI2::NoError. * * @return Returns code of occurred error or KGAPI2::NoError when no error * has occurred. */ KGAPI2::Error error() const; /** * @brief Error string * * This method can only be called after the job has emitted Job::finished * signal. Calling this method on a running job will always return an empty * string. * * @return Returns localized description of error or an empty string if no * error has occurred. */ QString errorString() const; /** * @brief Set maximum quota timeout * * Sets maximum interval for which the job should wait before trying to submit * a request that has previously failed due to exceeded quota. * * Default timeout is 1 seconds, then after every failed request the timeout * is increased exponentially until reaching @p maxTimeout. * * @param maxTimeout Maximum timeout (in seconds), or @p -1 for no timeout */ void setMaxTimeout(int maxTimeout); /** * @brief Maximum quota limit. * * @return Returns maximum timeout in seconds or -1 if there is no timeout set. * @see Job::setMaxTimeout */ int maxTimeout() const; /** * @brief Whether job is running * * A job is considered running from the moment it's started until * until Job::finished is emitted. Some methods should not be * called when a job is running. * * @return Returns whether this job is currently running. * @sa start() */ bool isRunning() const; /** * @brief Set account to be used to authenticate requests * * By default, no account is set and all request are sent without any * authentication. * * @param account Account to use */ void setAccount(const AccountPtr &account); /** * @brief Returns account used to authenticate requests * * For jobs that don't require authentication, this method returns a null * pointer. * * @return Am Account or a null pointer when no account was set. */ AccountPtr account() const; + /** + * @brief Sets whether response will have indentations and line breaks. + * + * When this is false, it can reduce the response payload size, + * which might lead to better performance in some environments. + * Default is false. + * + * @param prettyPrint + */ + void setPrettyPrint(bool prettyPrint); + + /** + * @brief Returns prettyPrint query parameter. + * + * @return prettyPrint query parameter + */ + bool prettyPrint() const; + /** * @brief Restarts this job * * When a job finishes, it's possible to run it again, without having * to create a new job. * * The job will throw away all results retrieved in previous run and retrieve * everything again. - * + * * @see Job::aboutToStart */ void restart(); Q_SIGNALS: /** * @brief Emitted when @p job has finished * * The signal is emitted every time, no matter whether the job is successful * or an error has occurred. * * Subclasses should never ever emit this signal directly. * Use Job::emitFinished instead. * * @param job The job that has finished * @sa emitFinished() */ void finished(KGAPI2::Job *job); /** * @brief Emitted when a job progress changes. * * Note that some jobs might not provide progress information, thus this * signal will never be emitted. * * @param job The job that the information relates to * @param processed Amount of already processed items * @param total Total amount of items to process */ void progress(KGAPI2::Job *job, int processed, int total); protected: /** * @brief Set job error to @p error * * @param error Error code to set * @see Job::error */ void setError(KGAPI2::Error error); /** * @brief Set job error description to @p errorString * * @param errorString Error description to set * @see Job::errorString */ void setErrorString(const QString &errorString); /** * @brief Emits Job::finished() signal * * Subclasses should always use this method instead of directly emitting * Job::finished(). */ virtual void emitFinished(); /** * @brief This method is invoked right before finished() is emitted * * Subclasses can reimplement this method to do a final cleanup before * the Job::finished() signal is emitted. * * @note Note that after Job::finished() the job is not running anymore and * therefore the job should not modify any data accessible by user. */ virtual void aboutToFinish(); /** * @brief Emit progress() signal * * Subclasses should always use this method instead of directly emitting * Job::progress(). * * @param processed Amount of already processed items * @param total Total amount of items to process */ virtual void emitProgress(int processed, int total); /** * @brief This method is invoked right before Job::start() is called. * * Subclasses should reset their internal state and call parent implementation. */ virtual void aboutToStart(); /** * @brief This method is invoked when job is started. * * Job is automatically started when application enters event loop. */ virtual void start() = 0; /** * @brief Dispatches @p request via @p accessManager * * Because different types of request require different HTTP method to be * used, subclasses must reimplement this method and use respective HTTP * method to send the @p request via @p accessManager. * * @param accessManager QNetworkAccessManager used to dispatch the request * @param request Request to dispatch * @param data Data to sent in the body of the request * @param contentType Content-Type of @p data */ virtual void dispatchRequest(QNetworkAccessManager *accessManager, const QNetworkRequest &request, const QByteArray &data, const QString &contentType) = 0; /** * @brief Called when a reply is received. * * Sublcasses must reimplement this method to handle reply content. * * @param reply A reply received from server * @param rawData Raw content of the reply. Don't use QNetworkReply::readAll, * because this method has already been called by Job and thus it would * return nothing. */ virtual void handleReply(const QNetworkReply *reply, const QByteArray &rawData) = 0; /** * @brief Enqueues @p request in dispatcher queue * * Subclasses should call this method to enqueue the @p request in main job * queue. The request is automatically dispatched, and reply is handled. * Authorization headers and standars query parameters will be applied. * * @param request Request to enqueue * @param data Data to be send in body of the request * @param contentType Content type of @p data */ virtual void enqueueRequest(const QNetworkRequest &request, const QByteArray &data = QByteArray(), const QString &contentType = QString()); private: class Private; Private * const d; friend class Private; friend class AuthJob; }; } // namespace KGAPI2 #endif // LIBKGAPI2_JOB_H diff --git a/src/core/job_p.h b/src/core/job_p.h index c418938..9375141 100644 --- a/src/core/job_p.h +++ b/src/core/job_p.h @@ -1,93 +1,94 @@ /* * This file is part of LibKGAPI library * * Copyright (C) 2013 Daniel Vrátil * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef KGAPI_JOB_P_H #define KGAPI_JOB_P_H #include "job.h" #include #include #include #include class QFile; namespace KGAPI2 { struct Request { QNetworkRequest request; QByteArray rawData; QString contentType; }; class Q_DECL_HIDDEN FileLogger { public: ~FileLogger(); static FileLogger *self(); void logRequest(const QNetworkRequest &request, const QByteArray &rawData); void logReply(const QNetworkReply *reply, const QByteArray &rawData); private: FileLogger(); QScopedPointer mFile; static FileLogger *sInstance; }; class Q_DECL_HIDDEN Job::Private { public: Private(Job *parent); void init(); QString parseErrorMessage(const QByteArray &json); void _k_doStart(); void _k_doEmitFinished(); void _k_replyReceived(QNetworkReply *reply); void _k_dispatchTimeout(); bool isRunning; Error error; QString errorString; AccountPtr account; QNetworkAccessManager *accessManager; QQueue requestQueue; QTimer *dispatchTimer; int maxTimeout; + bool prettyPrint; Request currentRequest; private: Job * const q; }; } #endif // KGAPI_JOB_P_H