File indexing completed on 2024-05-12 05:22:28

0001 /*
0002  *  SPDX-FileCopyrightText: 2022 Claudio Cambra <claudio.cambra@kde.org>
0003  *
0004  *  SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0005 */
0006 
0007 #include "peopleservice.h"
0008 #include "contactgroup.h"
0009 #include "debug.h"
0010 #include "person.h"
0011 
0012 #include <QJsonDocument>
0013 #include <QJsonObject>
0014 #include <QJsonArray>
0015 #include <QUrlQuery>
0016 
0017 /* Qt::escape() */
0018 #include <QTextDocument>
0019 
0020 namespace KGAPI2::People
0021 {
0022 
0023 namespace PeopleService
0024 {
0025 
0026 namespace Private
0027 {
0028 
0029 enum FetchType {
0030     PersonFetch,
0031     ContactGroupFetch
0032 };
0033 
0034 static const QUrl GoogleApisUrl(QStringLiteral("https://people.googleapis.com"));
0035 static const auto PeopleV1Path = QStringLiteral("/v1/");
0036 static const auto PeopleBasePath = QString(PeopleV1Path % QStringLiteral("people"));
0037 static const auto ContactGroupsBasePath = QString(PeopleV1Path % QStringLiteral("contactGroups"));
0038 
0039 static const auto AllPersonFields = QStringLiteral("addresses,"
0040                                                    "ageRanges,"
0041                                                    "biographies,"
0042                                                    "birthdays,"
0043                                                    "calendarUrls,"
0044                                                    "clientData,"
0045                                                    "coverPhotos,"
0046                                                    "emailAddresses,"
0047                                                    "events,"
0048                                                    "externalIds,"
0049                                                    "genders,"
0050                                                    "imClients,"
0051                                                    "interests,"
0052                                                    "locales,"
0053                                                    "locations,"
0054                                                    "memberships,"
0055                                                    "metadata,"
0056                                                    "miscKeywords,"
0057                                                    "names,"
0058                                                    "nicknames,"
0059                                                    "occupations,"
0060                                                    "organizations,"
0061                                                    "phoneNumbers,"
0062                                                    "photos,"
0063                                                    "relations,"
0064                                                    "sipAddresses,"
0065                                                    "skills,"
0066                                                    "urls,"
0067                                                    "userDefined");
0068 
0069 static const auto AllUpdatablePersonFields = QStringLiteral("addresses,"
0070                                                             "biographies,"
0071                                                             "birthdays,"
0072                                                             "calendarUrls,"
0073                                                             "clientData,"
0074                                                             "emailAddresses,"
0075                                                             "events,"
0076                                                             "externalIds,"
0077                                                             "genders,"
0078                                                             "imClients,"
0079                                                             "interests,"
0080                                                             "locales,"
0081                                                             "locations,"
0082                                                             "memberships,"
0083                                                             "miscKeywords,"
0084                                                             "names,"
0085                                                             "nicknames,"
0086                                                             "occupations,"
0087                                                             "organizations,"
0088                                                             "phoneNumbers,"
0089                                                             "relations,"
0090                                                             "sipAddresses,"
0091                                                             "urls,"
0092                                                             "userDefined");
0093 
0094 static const auto AllGroupFields = QStringLiteral("clientData,"
0095                                                   "groupType,"
0096                                                   "memberCount,"
0097                                                   "metadata,"
0098                                                   "name");
0099 
0100 static const auto AllRecentlyCreatedAvailableGroupFields = QStringLiteral("clientData,"
0101                                                                           "groupType,"
0102                                                                           "metadata,"
0103                                                                           "name");
0104 
0105 void writeNextPageDataQuery(FetchType fetchType, FeedData &feedData, const QJsonObject &replyRootObject, const QString &syncToken = {})
0106 {
0107     if(!replyRootObject.contains(QStringLiteral("nextPageToken"))) {
0108         return;
0109     }
0110 
0111     QUrl url;
0112     if (fetchType == PersonFetch) {
0113         url = fetchAllContactsUrl(syncToken);
0114     } else if (fetchType == ContactGroupFetch) {
0115         url = fetchAllContactGroupsUrl();
0116     } else {
0117         qCDebug(KGAPIDebug) << "Unknown type of fetch, cannot write next page data query";
0118         return;
0119     }
0120 
0121     QUrlQuery query(url);
0122     query.addQueryItem(QStringLiteral("pageToken"), replyRootObject.value(QStringLiteral("nextPageToken")).toString());
0123 
0124     url.setQuery(query);
0125     feedData.nextPageUrl = url;
0126 }
0127 
0128 } // Private
0129 
0130 QString allPersonFields()
0131 {
0132     return Private::AllPersonFields;
0133 }
0134 
0135 QString allUpdatablePersonFields()
0136 {
0137     return Private::AllUpdatablePersonFields;
0138 }
0139 
0140 QString allContactGroupRecentlyCreatedAvailableFields()
0141 {
0142     return Private::AllRecentlyCreatedAvailableGroupFields;
0143 }
0144 
0145 ObjectPtr JSONToPerson(const QByteArray &jsonData)
0146 {
0147     QJsonDocument document = QJsonDocument::fromJson(jsonData);
0148     if(document.isObject()) {
0149         const auto objectifiedDocument = document.object();
0150         const auto resourceName = objectifiedDocument.value(QStringLiteral("resourceName")).toString();
0151 
0152         return resourceName.startsWith(QStringLiteral("people")) ? People::Person::fromJSON(objectifiedDocument) : People::PersonPtr();
0153     }
0154 
0155     return People::PersonPtr();
0156 }
0157 
0158 QUrl fetchAllContactsUrl(const QString &syncToken)
0159 {
0160     QUrl url(Private::GoogleApisUrl);
0161     const QString path = Private::PeopleBasePath % QStringLiteral("/me/connections");
0162     url.setPath(path);
0163 
0164     QUrlQuery query(url);
0165     query.addQueryItem(QStringLiteral("personFields"), Private::AllPersonFields);
0166     query.addQueryItem(QStringLiteral("requestSyncToken"), QStringLiteral("true"));
0167 
0168     if (!syncToken.isEmpty()) {
0169         query.addQueryItem(QStringLiteral("syncToken"), syncToken);
0170     }
0171 
0172     url.setQuery(query);
0173     return url;
0174 }
0175 
0176 // https://developers.google.com/people/api/rest/v1/people/searchContacts
0177 QUrl fetchContactUrl(const QString &resourceName)
0178 {
0179     QUrl url(Private::GoogleApisUrl);
0180     const QString path = Private::PeopleV1Path % resourceName;
0181     url.setPath(path);
0182 
0183     QUrlQuery query(url);
0184     query.addQueryItem(QStringLiteral("personFields"), Private::AllPersonFields);
0185 
0186     url.setQuery(query);
0187     return url;
0188 }
0189 
0190 QUrl createContactUrl()
0191 {
0192     QUrl url(Private::GoogleApisUrl);
0193     const QString path = Private::PeopleBasePath % QStringLiteral(":createContact");
0194     url.setPath(path);
0195     return url;
0196 }
0197 
0198 QUrl updateContactUrl(const QString &resourceName, const QString &personFields)
0199 {
0200     QUrl url(Private::GoogleApisUrl);
0201     url.setPath(Private::PeopleV1Path % resourceName % QStringLiteral(":updateContact"));
0202 
0203     QUrlQuery query(url);
0204     query.addQueryItem(QStringLiteral("updatePersonFields"), personFields);
0205 
0206     url.setQuery(query);
0207     return url;
0208 }
0209 
0210 QUrl deleteContactUrl(const QString &resourceName)
0211 {
0212     QUrl url(Private::GoogleApisUrl);
0213     url.setPath(Private::PeopleV1Path % resourceName % QStringLiteral(":deleteContact"));
0214     return url;
0215 }
0216 
0217 QUrl fetchAllContactGroupsUrl()
0218 {
0219     QUrl url(Private::GoogleApisUrl);
0220     url.setPath(Private::ContactGroupsBasePath);
0221 
0222     QUrlQuery query(url);
0223     query.addQueryItem(QStringLiteral("groupFields"), Private::AllGroupFields);
0224 
0225     url.setQuery(query);
0226 
0227     return url;
0228 }
0229 
0230 // https://developers.google.com/people/api/rest/v1/contactGroups/get
0231 QUrl fetchContactGroupUrl(const QString &resourceName)
0232 {
0233     QUrl url(Private::GoogleApisUrl);
0234     const QString path = Private::PeopleV1Path % resourceName;
0235     url.setPath(path);
0236 
0237     QUrlQuery query(url);
0238     query.addQueryItem(QStringLiteral("groupFields"), Private::AllGroupFields);
0239 
0240     url.setQuery(query);
0241     return url;
0242 }
0243 
0244 QUrl createContactGroupUrl()
0245 {
0246     QUrl url(Private::GoogleApisUrl);
0247     url.setPath(Private::ContactGroupsBasePath);
0248     return url;
0249 }
0250 
0251 QUrl updateContactGroupUrl(const QString &resourceName)
0252 {
0253     QUrl url(Private::GoogleApisUrl);
0254     const QString path = Private::PeopleV1Path % resourceName;
0255     url.setPath(path);
0256     return url;
0257 }
0258 
0259 QUrl deleteContactGroupUrl(const QString &resourceName, const bool deleteContacts)
0260 {
0261     QUrl url(Private::GoogleApisUrl);
0262     url.setPath(Private::PeopleV1Path % resourceName);
0263 
0264     const auto deleteContactsString = deleteContacts ? QStringLiteral("true") : QStringLiteral("false");
0265     QUrlQuery query(url);
0266     query.addQueryItem(QStringLiteral("deleteContacts"), deleteContactsString);
0267 
0268     url.setQuery(query);
0269     return url;
0270 }
0271 
0272 QUrl updateContactPhotoUrl(const QString &resourceName)
0273 {
0274     QUrl url(Private::GoogleApisUrl);
0275     url.setPath(Private::PeopleV1Path % resourceName % QStringLiteral(":updateContactPhoto"));
0276     return url;
0277 }
0278 
0279 QUrl deleteContactPhotoUrl(const QString &resourceName, const QString &personFields)
0280 {
0281     QUrl url(Private::GoogleApisUrl);
0282     url.setPath(Private::PeopleV1Path % resourceName % QStringLiteral(":deleteContactPhoto"));
0283 
0284     QUrlQuery query(url);
0285     query.addQueryItem(QStringLiteral("personFields"), personFields);
0286 
0287     url.setQuery(query);
0288     return url;
0289 }
0290 
0291 ObjectsList parseConnectionsJSONFeed(FeedData &feedData, const QByteArray &jsonFeed, const QString &syncToken)
0292 {
0293     const auto document = QJsonDocument::fromJson(jsonFeed);
0294 
0295     if (!document.isObject()) {
0296         return {};
0297     }
0298 
0299     ObjectsList output;
0300 
0301     const auto rootObject = document.object();
0302     const auto connections = rootObject.value(QStringLiteral("connections")).toArray();
0303     for(const auto &connection : connections) {
0304         output.append(People::Person::fromJSON(connection.toObject()));
0305     }
0306 
0307     feedData.totalResults = rootObject.value(QStringLiteral("totalItems")).toInt();
0308 
0309     Private::writeNextPageDataQuery(Private::PersonFetch, feedData, rootObject, syncToken);
0310     feedData.syncToken = rootObject.value(QStringLiteral("nextSyncToken")).toString();
0311 
0312     return output;
0313 }
0314 
0315 ObjectsList parseContactGroupsJSONFeed(FeedData &feedData, const QByteArray &jsonFeed)
0316 {
0317     // qDebug() << jsonFeed;
0318     const auto document = QJsonDocument::fromJson(jsonFeed);
0319 
0320     if (!document.isObject()) {
0321         return {};
0322     }
0323 
0324     ObjectsList output;
0325 
0326     const auto rootObject = document.object();
0327     const auto contactGroups = rootObject.value(QStringLiteral("contactGroups")).toArray();
0328     for(const auto &contactGroup : contactGroups) {
0329         output.append(People::ContactGroup::fromJSON(contactGroup.toObject()));
0330     }
0331 
0332     feedData.totalResults = rootObject.value(QStringLiteral("totalItems")).toInt();
0333 
0334     Private::writeNextPageDataQuery(Private::ContactGroupFetch, feedData, rootObject);
0335 
0336     return output;
0337 }
0338 
0339 }
0340 
0341 namespace PeopleUtils
0342 {
0343 
0344 void addValueToJsonObjectIfValid(QJsonObject &object, const QByteArray &key, const int value)
0345 {
0346     object.insert(QString::fromUtf8(key), value);
0347 }
0348 
0349 void addValueToJsonObjectIfValid(QJsonObject &object, const QByteArray &key, const bool value)
0350 {
0351     object.insert(QString::fromUtf8(key), value);
0352 }
0353 
0354 void addValueToJsonObjectIfValid(QJsonObject &object, const QByteArray &key, const QString &value)
0355 {
0356     if (!value.isEmpty()) {
0357         object.insert(QString::fromUtf8(key), value);
0358     }
0359 }
0360 
0361 void addValueToJsonObjectIfValid(QJsonObject &object, const QByteArray &key, const QJsonValue &value)
0362 {
0363     if (!value.isNull() || !value.isUndefined()) {
0364         object.insert(QString::fromUtf8(key), value);
0365     }
0366 }
0367 
0368 
0369 }
0370 
0371 }