Kgapi

peopleservice.cpp
1/*
2 * SPDX-FileCopyrightText: 2022 Claudio Cambra <claudio.cambra@kde.org>
3 *
4 * SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
5*/
6
7#include "peopleservice.h"
8#include "contactgroup.h"
9#include "debug.h"
10#include "person.h"
11
12#include <QJsonDocument>
13#include <QJsonObject>
14#include <QJsonArray>
15#include <QUrlQuery>
16
17/* Qt::escape() */
18#include <QTextDocument>
19
20namespace KGAPI2::People
21{
22
23namespace PeopleService
24{
25
26namespace Private
27{
28
29enum FetchType {
30 PersonFetch,
31 ContactGroupFetch
32};
33
34static const QUrl GoogleApisUrl(QStringLiteral("https://people.googleapis.com"));
35static const auto PeopleV1Path = QStringLiteral("/v1/");
36static const auto PeopleBasePath = QString(PeopleV1Path % QStringLiteral("people"));
37static const auto ContactGroupsBasePath = QString(PeopleV1Path % QStringLiteral("contactGroups"));
38
39static const auto AllPersonFields = QStringLiteral("addresses,"
40 "ageRanges,"
41 "biographies,"
42 "birthdays,"
43 "calendarUrls,"
44 "clientData,"
45 "coverPhotos,"
46 "emailAddresses,"
47 "events,"
48 "externalIds,"
49 "genders,"
50 "imClients,"
51 "interests,"
52 "locales,"
53 "locations,"
54 "memberships,"
55 "metadata,"
56 "miscKeywords,"
57 "names,"
58 "nicknames,"
59 "occupations,"
60 "organizations,"
61 "phoneNumbers,"
62 "photos,"
63 "relations,"
64 "sipAddresses,"
65 "skills,"
66 "urls,"
67 "userDefined");
68
69static const auto AllUpdatablePersonFields = QStringLiteral("addresses,"
70 "biographies,"
71 "birthdays,"
72 "calendarUrls,"
73 "clientData,"
74 "emailAddresses,"
75 "events,"
76 "externalIds,"
77 "genders,"
78 "imClients,"
79 "interests,"
80 "locales,"
81 "locations,"
82 "memberships,"
83 "miscKeywords,"
84 "names,"
85 "nicknames,"
86 "occupations,"
87 "organizations,"
88 "phoneNumbers,"
89 "relations,"
90 "sipAddresses,"
91 "urls,"
92 "userDefined");
93
94static const auto AllGroupFields = QStringLiteral("clientData,"
95 "groupType,"
96 "memberCount,"
97 "metadata,"
98 "name");
99
100static const auto AllRecentlyCreatedAvailableGroupFields = QStringLiteral("clientData,"
101 "groupType,"
102 "metadata,"
103 "name");
104
105void writeNextPageDataQuery(FetchType fetchType, FeedData &feedData, const QJsonObject &replyRootObject, const QString &syncToken = {})
106{
107 if(!replyRootObject.contains(QStringLiteral("nextPageToken"))) {
108 return;
109 }
110
111 QUrl url;
112 if (fetchType == PersonFetch) {
113 url = fetchAllContactsUrl(syncToken);
114 } else if (fetchType == ContactGroupFetch) {
115 url = fetchAllContactGroupsUrl();
116 } else {
117 qCDebug(KGAPIDebug) << "Unknown type of fetch, cannot write next page data query";
118 return;
119 }
120
121 QUrlQuery query(url);
122 query.addQueryItem(QStringLiteral("pageToken"), replyRootObject.value(QStringLiteral("nextPageToken")).toString());
123
124 url.setQuery(query);
125 feedData.nextPageUrl = url;
126}
127
128} // Private
129
130QString allPersonFields()
131{
132 return Private::AllPersonFields;
133}
134
135QString allUpdatablePersonFields()
136{
137 return Private::AllUpdatablePersonFields;
138}
139
140QString allContactGroupRecentlyCreatedAvailableFields()
141{
142 return Private::AllRecentlyCreatedAvailableGroupFields;
143}
144
145ObjectPtr JSONToPerson(const QByteArray &jsonData)
146{
147 QJsonDocument document = QJsonDocument::fromJson(jsonData);
148 if(document.isObject()) {
149 const auto objectifiedDocument = document.object();
150 const auto resourceName = objectifiedDocument.value(QStringLiteral("resourceName")).toString();
151
152 return resourceName.startsWith(QStringLiteral("people")) ? People::Person::fromJSON(objectifiedDocument) : People::PersonPtr();
153 }
154
155 return People::PersonPtr();
156}
157
158QUrl fetchAllContactsUrl(const QString &syncToken)
159{
160 QUrl url(Private::GoogleApisUrl);
161 const QString path = Private::PeopleBasePath % QStringLiteral("/me/connections");
162 url.setPath(path);
163
164 QUrlQuery query(url);
165 query.addQueryItem(QStringLiteral("personFields"), Private::AllPersonFields);
166 query.addQueryItem(QStringLiteral("requestSyncToken"), QStringLiteral("true"));
167
168 if (!syncToken.isEmpty()) {
169 query.addQueryItem(QStringLiteral("syncToken"), syncToken);
170 }
171
172 url.setQuery(query);
173 return url;
174}
175
176// https://developers.google.com/people/api/rest/v1/people/searchContacts
177QUrl fetchContactUrl(const QString &resourceName)
178{
179 QUrl url(Private::GoogleApisUrl);
180 const QString path = Private::PeopleV1Path % resourceName;
181 url.setPath(path);
182
183 QUrlQuery query(url);
184 query.addQueryItem(QStringLiteral("personFields"), Private::AllPersonFields);
185
186 url.setQuery(query);
187 return url;
188}
189
190QUrl createContactUrl()
191{
192 QUrl url(Private::GoogleApisUrl);
193 const QString path = Private::PeopleBasePath % QStringLiteral(":createContact");
194 url.setPath(path);
195 return url;
196}
197
198QUrl updateContactUrl(const QString &resourceName, const QString &personFields)
199{
200 QUrl url(Private::GoogleApisUrl);
201 url.setPath(Private::PeopleV1Path % resourceName % QStringLiteral(":updateContact"));
202
203 QUrlQuery query(url);
204 query.addQueryItem(QStringLiteral("updatePersonFields"), personFields);
205
206 url.setQuery(query);
207 return url;
208}
209
210QUrl deleteContactUrl(const QString &resourceName)
211{
212 QUrl url(Private::GoogleApisUrl);
213 url.setPath(Private::PeopleV1Path % resourceName % QStringLiteral(":deleteContact"));
214 return url;
215}
216
217QUrl fetchAllContactGroupsUrl()
218{
219 QUrl url(Private::GoogleApisUrl);
220 url.setPath(Private::ContactGroupsBasePath);
221
222 QUrlQuery query(url);
223 query.addQueryItem(QStringLiteral("groupFields"), Private::AllGroupFields);
224
225 url.setQuery(query);
226
227 return url;
228}
229
230// https://developers.google.com/people/api/rest/v1/contactGroups/get
231QUrl fetchContactGroupUrl(const QString &resourceName)
232{
233 QUrl url(Private::GoogleApisUrl);
234 const QString path = Private::PeopleV1Path % resourceName;
235 url.setPath(path);
236
237 QUrlQuery query(url);
238 query.addQueryItem(QStringLiteral("groupFields"), Private::AllGroupFields);
239
240 url.setQuery(query);
241 return url;
242}
243
244QUrl createContactGroupUrl()
245{
246 QUrl url(Private::GoogleApisUrl);
247 url.setPath(Private::ContactGroupsBasePath);
248 return url;
249}
250
251QUrl updateContactGroupUrl(const QString &resourceName)
252{
253 QUrl url(Private::GoogleApisUrl);
254 const QString path = Private::PeopleV1Path % resourceName;
255 url.setPath(path);
256 return url;
257}
258
259QUrl deleteContactGroupUrl(const QString &resourceName, const bool deleteContacts)
260{
261 QUrl url(Private::GoogleApisUrl);
262 url.setPath(Private::PeopleV1Path % resourceName);
263
264 const auto deleteContactsString = deleteContacts ? QStringLiteral("true") : QStringLiteral("false");
265 QUrlQuery query(url);
266 query.addQueryItem(QStringLiteral("deleteContacts"), deleteContactsString);
267
268 url.setQuery(query);
269 return url;
270}
271
272QUrl updateContactPhotoUrl(const QString &resourceName)
273{
274 QUrl url(Private::GoogleApisUrl);
275 url.setPath(Private::PeopleV1Path % resourceName % QStringLiteral(":updateContactPhoto"));
276 return url;
277}
278
279QUrl deleteContactPhotoUrl(const QString &resourceName, const QString &personFields)
280{
281 QUrl url(Private::GoogleApisUrl);
282 url.setPath(Private::PeopleV1Path % resourceName % QStringLiteral(":deleteContactPhoto"));
283
284 QUrlQuery query(url);
285 query.addQueryItem(QStringLiteral("personFields"), personFields);
286
287 url.setQuery(query);
288 return url;
289}
290
291ObjectsList parseConnectionsJSONFeed(FeedData &feedData, const QByteArray &jsonFeed, const QString &syncToken)
292{
293 const auto document = QJsonDocument::fromJson(jsonFeed);
294
295 if (!document.isObject()) {
296 return {};
297 }
298
299 ObjectsList output;
300
301 const auto rootObject = document.object();
302 const auto connections = rootObject.value(QStringLiteral("connections")).toArray();
303 for(const auto &connection : connections) {
304 output.append(People::Person::fromJSON(connection.toObject()));
305 }
306
307 feedData.totalResults = rootObject.value(QStringLiteral("totalItems")).toInt();
308
309 Private::writeNextPageDataQuery(Private::PersonFetch, feedData, rootObject, syncToken);
310 feedData.syncToken = rootObject.value(QStringLiteral("nextSyncToken")).toString();
311
312 return output;
313}
314
315ObjectsList parseContactGroupsJSONFeed(FeedData &feedData, const QByteArray &jsonFeed)
316{
317 // qDebug() << jsonFeed;
318 const auto document = QJsonDocument::fromJson(jsonFeed);
319
320 if (!document.isObject()) {
321 return {};
322 }
323
324 ObjectsList output;
325
326 const auto rootObject = document.object();
327 const auto contactGroups = rootObject.value(QStringLiteral("contactGroups")).toArray();
328 for(const auto &contactGroup : contactGroups) {
329 output.append(People::ContactGroup::fromJSON(contactGroup.toObject()));
330 }
331
332 feedData.totalResults = rootObject.value(QStringLiteral("totalItems")).toInt();
333
334 Private::writeNextPageDataQuery(Private::ContactGroupFetch, feedData, rootObject);
335
336 return output;
337}
338
339}
340
341namespace PeopleUtils
342{
343
344void addValueToJsonObjectIfValid(QJsonObject &object, const QByteArray &key, const int value)
345{
346 object.insert(QString::fromUtf8(key), value);
347}
348
349void addValueToJsonObjectIfValid(QJsonObject &object, const QByteArray &key, const bool value)
350{
351 object.insert(QString::fromUtf8(key), value);
352}
353
354void addValueToJsonObjectIfValid(QJsonObject &object, const QByteArray &key, const QString &value)
355{
356 if (!value.isEmpty()) {
357 object.insert(QString::fromUtf8(key), value);
358 }
359}
360
361void addValueToJsonObjectIfValid(QJsonObject &object, const QByteArray &key, const QJsonValue &value)
362{
363 if (!value.isNull() || !value.isUndefined()) {
364 object.insert(QString::fromUtf8(key), value);
365 }
366}
367
368
369}
370
371}
Structure to store additional information about a feed.
Definition types.h:24
int totalResults
Number of all items.
Definition types.h:37
QString syncToken
Sync token that can be used for incremental updates by some of the services.
Definition types.h:42
QUrl nextPageUrl
Link to next page of feed.
Definition types.h:38
QJsonDocument fromJson(const QByteArray &json, QJsonParseError *error)
bool isObject() const const
QJsonObject object() const const
bool contains(QLatin1StringView key) const const
QJsonValue value(QLatin1StringView key) const const
bool isNull() const const
bool isUndefined() const const
QString toString() const const
void append(QList< T > &&value)
T value(qsizetype i) const const
QString fromUtf8(QByteArrayView str)
bool isEmpty() const const
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
void setPath(const QString &path, ParsingMode mode)
void setQuery(const QString &query, ParsingMode mode)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri May 3 2024 11:50:41 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.