diff --git a/autotests/drive/aboutfetchjobtest.cpp b/autotests/drive/aboutfetchjobtest.cpp --- a/autotests/drive/aboutfetchjobtest.cpp +++ b/autotests/drive/aboutfetchjobtest.cpp @@ -17,7 +17,7 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ - +#include #include #include @@ -32,7 +32,12 @@ using namespace KGAPI2; +namespace { + static const char *LimitedFieldTag = "limited fields"; +} + Q_DECLARE_METATYPE(QList) +Q_DECLARE_METATYPE(KGAPI2::Drive::AboutPtr) class AboutFetchJobTest : public QObject { @@ -43,17 +48,40 @@ NetworkAccessManagerFactory::setFactory(new FakeNetworkAccessManagerFactory); } + void testFetch_data() + { + QTest::addColumn>("scenarios"); + QTest::addColumn("about"); + + QTest::newRow("") + << QList{ + scenarioFromFile(QFINDTESTDATA("data/about_fetch_request.txt"), + QFINDTESTDATA("data/about_fetch_response.txt")) + } + << aboutFromFile(QFINDTESTDATA("data/about.json")); + + QTest::newRow(LimitedFieldTag) + << QList{ + scenarioFromFile(QFINDTESTDATA("data/about_fetch_limited_fields_request.txt"), + QFINDTESTDATA("data/about_fetch_limited_fields_response.txt")) + } + << aboutFromFile(QFINDTESTDATA("data/about_limited_fields.json")); + } + void testFetch() { - FakeNetworkAccessManagerFactory::get()->setScenarios({ - scenarioFromFile(QFINDTESTDATA("data/about_fetch_request.txt"), - QFINDTESTDATA("data/about_fetch_response.txt")) + QFETCH(QList, scenarios); + QFETCH(KGAPI2::Drive::AboutPtr, about); - }); - const auto about = aboutFromFile(QFINDTESTDATA("data/about.json")); + FakeNetworkAccessManagerFactory::get()->setScenarios(scenarios); auto account = AccountPtr::create(QStringLiteral("MockAccount"), QStringLiteral("MockToken")); auto job = new Drive::AboutFetchJob(account, nullptr); + + if (strcmp(LimitedFieldTag, QTest::currentDataTag()) == 0) { + job->setFields({ Drive::About::KindField, Drive::About::PermissionIdField }); + } + QVERIFY(execJob(job)); const auto items = job->items(); QCOMPARE(items.count(), 1); diff --git a/autotests/drive/data/about_fetch_limited_fields_request.txt b/autotests/drive/data/about_fetch_limited_fields_request.txt new file mode 100644 --- /dev/null +++ b/autotests/drive/data/about_fetch_limited_fields_request.txt @@ -0,0 +1 @@ +GET https://www.googleapis.com/drive/v2/about?includeSubscribed=true&fields=kind,permissionId&prettyPrint=false diff --git a/autotests/drive/data/about_fetch_limited_fields_response.txt b/autotests/drive/data/about_fetch_limited_fields_response.txt new file mode 100644 --- /dev/null +++ b/autotests/drive/data/about_fetch_limited_fields_response.txt @@ -0,0 +1,7 @@ +HTTP/1.1 200 OK +content-type: application/json; charset=UTF-8 + +{ + "kind": "drive#about", + "permissionId": "07857126568572968161" +} diff --git a/autotests/drive/data/about_fetch_request.txt b/autotests/drive/data/about_fetch_request.txt --- 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/about_limited_fields.json b/autotests/drive/data/about_limited_fields.json new file mode 100644 --- /dev/null +++ b/autotests/drive/data/about_limited_fields.json @@ -0,0 +1,4 @@ +{ + "kind": "drive#about", + "permissionId": "07857126568572968161" +} diff --git a/src/core/job.h b/src/core/job.h --- a/src/core/job.h +++ b/src/core/job.h @@ -27,6 +27,7 @@ #include "kgapicore_export.h" #include +#include class QNetworkAccessManager; class QNetworkReply; @@ -40,12 +41,13 @@ * * Usual workflow of Job subclasses is to reimplement Job::start, * Job::dispatchRequest and Job::handleReply, then enqueue a QNetworkRequest using - * Job::enqueueRequest. 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::enqueueRequest. An authorized request is buildable with Job::authorizedRequest. + * 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 when program enters an event loop. + * Job is automatically started when program enters an event loop. * * @author Daniel Vrátil * @since 2.0 @@ -72,7 +74,7 @@ * @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 + * 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 @@ -178,6 +180,40 @@ */ 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 Set subset of fields to include in the response. + * + * Use for better performance. + * + * @param fields List of fields + */ + void setFields(const QStringList &fields); + + /** + * @brief Returns fields selector. + * + * @return List of fields + */ + QStringList fields() const; + /** * @brief Restarts this job * @@ -186,7 +222,7 @@ * * The job will throw away all results retrieved in previous run and retrieve * everything again. - * + * * @see Job::aboutToStart */ void restart(); @@ -308,6 +344,17 @@ */ virtual void handleReply(const QNetworkReply *reply, const QByteArray &rawData) = 0; + /** + * @brief Builds a request with headers and standard query parameters. + * + * Subclasses should use this method to get an authorized request with + * standard query parameters. + * + * @param url Api url + * @return Enqueuable request + */ + QNetworkRequest authorizedRequest(QUrl &url) const; + /** * @brief Enqueues @p request in dispatcher queue * diff --git a/src/core/job.cpp b/src/core/job.cpp --- a/src/core/job.cpp +++ b/src/core/job.cpp @@ -31,9 +31,15 @@ #include #include #include +#include using namespace KGAPI2; +namespace { + static const QString FieldsParam = QStringLiteral("fields"); + static const QString PrettyPrintParam = QStringLiteral("prettyPrint"); +} + FileLogger *FileLogger::sInstance = nullptr; FileLogger::FileLogger() @@ -102,6 +108,7 @@ error(KGAPI2::NoError), accessManager(nullptr), maxTimeout(0), + prettyPrint(false), q(parent) { } @@ -421,6 +428,56 @@ 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; +} + +QStringList Job::fields() const +{ + return d->fields; +} + +void Job::setFields(const QStringList &fields) +{ + if (d->isRunning) { + qCWarning(KGAPIDebug) << "Called setFields() on running job. Ignoring."; + return; + } + + d->fields = fields; +} + +QNetworkRequest Job::authorizedRequest(QUrl &url) const +{ + QNetworkRequest request; + if (d->account) { + request.setRawHeader("Authorization", "Bearer " + d->account->accessToken().toLatin1()); + } + + QUrlQuery query(url); + if (!d->fields.isEmpty()) { + query.addQueryItem(FieldsParam, d->fields.join(QStringLiteral(","))); + } + query.addQueryItem(PrettyPrintParam, d->prettyPrint ? QStringLiteral("true") : QStringLiteral("false")); + + + url.setQuery(query); + request.setUrl(url); + + return request; +} + void Job::restart() { if (d->isRunning) { diff --git a/src/core/job_p.h b/src/core/job_p.h --- a/src/core/job_p.h +++ b/src/core/job_p.h @@ -81,6 +81,8 @@ QQueue requestQueue; QTimer *dispatchTimer; int maxTimeout; + bool prettyPrint; + QStringList fields; Request currentRequest; diff --git a/src/drive/about.h b/src/drive/about.h --- a/src/drive/about.h +++ b/src/drive/about.h @@ -221,8 +221,54 @@ typedef QSharedPointer MaxUploadSizePtr; typedef QList MaxUploadSizesList; - explicit About(const About &other); - virtual ~About(); + static const QString AdditionalRoleInfoField; + static const QString AdditionalRolesField; + static const QString BackgroundImageLinkField; + static const QString BytesUsedField; + static const QString CanCreateTeamDrivesField; + static const QString ColorRgbField; + static const QString DisplayNameField; + static const QString DomainSharingPolicyField; + static const QString EmailAddressField; + static const QString EtagField; + static const QString ExportFormatsField; + static const QString FeatureNameField; + static const QString FeatureRateField; + static const QString FeaturesField; + static const QString FolderColorPaletteField; + static const QString IdField; + static const QString ImportFormatsField; + static const QString IsAuthenticatedUserField; + static const QString IsCurrentAppInstalledField; + static const QString KindField; + static const QString LanguageCodeField; + static const QString LargestChangeIdField; + static const QString MaxUploadSizesField; + static const QString NameField; + static const QString PermissionIdField; + static const QString PictureField; + static const QString PrimaryRoleField; + static const QString QuotaBytesByServiceField; + static const QString QuotaBytesTotalField; + static const QString QuotaBytesUsedField; + static const QString QuotaBytesUsedAggregateField; + static const QString QuotaBytesUsedInTrashField; + static const QString QuotaTypeField; + static const QString RemainingChangeIdsField; + static const QString RoleSetsField; + static const QString RootFolderIdField; + static const QString SelfLinkField; + static const QString ServiceNameField; + static const QString SizeField; + static const QString SourceField; + static const QString TargetsField; + static const QString TeamDriveThemesField; + static const QString TypeField; + static const QString UrlField; + static const QString UserField; + + About(const About &other); + ~About() override; bool operator==(const About &other) const; bool operator!=(const About &other) const { return !operator==(other); } @@ -340,10 +386,10 @@ static AboutPtr fromJSON(const QByteArray &jsonData); private: - explicit About(); + About(); class Private; - Private *const d; + QScopedPointer const d; friend class Private; }; diff --git a/src/drive/about.cpp b/src/drive/about.cpp --- a/src/drive/about.cpp +++ b/src/drive/about.cpp @@ -361,6 +361,52 @@ { } +const QString About::AdditionalRoleInfoField = QStringLiteral("additionalRoleInfo"); +const QString About::AdditionalRolesField = QStringLiteral("additionalRoles"); +const QString About::BackgroundImageLinkField = QStringLiteral("backgroundImageLink"); +const QString About::BytesUsedField = QStringLiteral("bytesUsed"); +const QString About::CanCreateTeamDrivesField = QStringLiteral("canCreateTeamDrives"); +const QString About::ColorRgbField = QStringLiteral("colorRgb"); +const QString About::DisplayNameField = QStringLiteral("displayName"); +const QString About::DomainSharingPolicyField = QStringLiteral("domainSharingPolicy"); +const QString About::EmailAddressField = QStringLiteral("emailAddress"); +const QString About::EtagField = QStringLiteral("etag"); +const QString About::ExportFormatsField = QStringLiteral("exportFormats"); +const QString About::FeatureNameField = QStringLiteral("featureName"); +const QString About::FeatureRateField = QStringLiteral("featureRate"); +const QString About::FeaturesField = QStringLiteral("features"); +const QString About::FolderColorPaletteField = QStringLiteral("folderColorPalette"); +const QString About::IdField = QStringLiteral("id"); +const QString About::ImportFormatsField = QStringLiteral("importFormats"); +const QString About::IsAuthenticatedUserField = QStringLiteral("isAuthenticatedUser"); +const QString About::IsCurrentAppInstalledField = QStringLiteral("isCurrentAppInstalled"); +const QString About::KindField = QStringLiteral("kind"); +const QString About::LanguageCodeField = QStringLiteral("languageCode"); +const QString About::LargestChangeIdField = QStringLiteral("largestChangeId"); +const QString About::MaxUploadSizesField = QStringLiteral("maxUploadSizes"); +const QString About::NameField = QStringLiteral("name"); +const QString About::PermissionIdField = QStringLiteral("permissionId"); +const QString About::PictureField = QStringLiteral("picture"); +const QString About::PrimaryRoleField = QStringLiteral("primaryRole"); +const QString About::QuotaBytesByServiceField = QStringLiteral("quotaBytesByService"); +const QString About::QuotaBytesTotalField = QStringLiteral("quotaBytesTotal"); +const QString About::QuotaBytesUsedAggregateField = QStringLiteral("quotaBytesUsedAggregate"); +const QString About::QuotaBytesUsedInTrashField = QStringLiteral("quotaBytesUsedInTrash"); +const QString About::QuotaBytesUsedField = QStringLiteral("quotaBytesUsed"); +const QString About::QuotaTypeField = QStringLiteral("quotaType"); +const QString About::RemainingChangeIdsField = QStringLiteral("remainingChangeIds"); +const QString About::RoleSetsField = QStringLiteral("roleSets"); +const QString About::RootFolderIdField = QStringLiteral("rootFolderId"); +const QString About::SelfLinkField = QStringLiteral("selfLink"); +const QString About::ServiceNameField = QStringLiteral("serviceName"); +const QString About::SizeField = QStringLiteral("size"); +const QString About::SourceField = QStringLiteral("source"); +const QString About::TargetsField = QStringLiteral("targets"); +const QString About::TeamDriveThemesField = QStringLiteral("teamDriveThemes"); +const QString About::TypeField = QStringLiteral("type"); +const QString About::UrlField = QStringLiteral("url"); +const QString About::UserField = QStringLiteral("user"); + About::About(): KGAPI2::Object(), d(new Private) @@ -373,10 +419,7 @@ { } -About::~About() -{ - delete d; -} +About::~About() = default; bool About::operator==(const About &other) const { diff --git a/src/drive/aboutfetchjob.cpp b/src/drive/aboutfetchjob.cpp --- a/src/drive/aboutfetchjob.cpp +++ b/src/drive/aboutfetchjob.cpp @@ -30,6 +30,7 @@ #include #include +#include using namespace KGAPI2; using namespace KGAPI2::Drive; @@ -118,9 +119,8 @@ void AboutFetchJob::start() { - QNetworkRequest request; - request.setRawHeader("Authorization", "Bearer " + account()->accessToken().toLatin1()); - request.setUrl(DriveService::fetchAboutUrl(d->includeSubscribed, d->maxChangeIdCount, d->startChangeId)); + QUrl url = DriveService::fetchAboutUrl(d->includeSubscribed, d->maxChangeIdCount, d->startChangeId); + QNetworkRequest request = authorizedRequest(url); enqueueRequest(request); } diff --git a/src/drive/driveservice.h b/src/drive/driveservice.h --- a/src/drive/driveservice.h +++ b/src/drive/driveservice.h @@ -43,7 +43,8 @@ */ KGAPIDRIVE_EXPORT QUrl fetchAboutUrl(bool includeSubscribed, qlonglong maxChangeIdCount, - qlonglong startChangeId); + qlonglong startChangeId, + QList > standardQueryParams); KGAPIDRIVE_EXPORT QUrl fetchAppUrl(const QString &appId); diff --git a/src/drive/driveservice.cpp b/src/drive/driveservice.cpp --- a/src/drive/driveservice.cpp +++ b/src/drive/driveservice.cpp @@ -40,11 +40,12 @@ namespace DriveService { -QUrl fetchAboutUrl(bool includeSubscribed, qlonglong maxChangeIdCount, qlonglong startChangeId) +QUrl fetchAboutUrl(bool includeSubscribed, qlonglong maxChangeIdCount, qlonglong startChangeId, QList > standardQueryParams) { QUrl url(Private::GoogleApisUrl); url.setPath(Private::AppsBasePath); QUrlQuery query(url); + query.setQueryItems(standardQueryParams); query.addQueryItem(QStringLiteral("includeSubscribed"), Utils::bool2Str(includeSubscribed)); if (maxChangeIdCount > 0) { query.addQueryItem(QStringLiteral("maxChangeIdCount"), QString::number(maxChangeIdCount));