summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRémi Nicole <nicole@kolabsystems.com>2018-03-27 18:26:11 +0200
committerChristian Mollekopf <chrigi_1@fastmail.fm>2018-03-27 18:26:15 +0200
commit761328989492db9bd603c2d7f1134d20e485d2f6 (patch)
tree0e3b4517dd2000fb1cc2738bbb22a3e54dfffb6f
parent80afd7070f2d8e57cab2fe55fef611623fdb75f0 (diff)
downloadsink-761328989492db9bd603c2d7f1134d20e485d2f6.tar.gz
sink-761328989492db9bd603c2d7f1134d20e485d2f6.zip
Add CalDAV support
Summary: Notes: - Add a `webdavcommon` folder for WebDAV generic resource code - Move `davresource` to `carddaveresource` and make it use the WebDAV code - For now it tests the CalDAV resource directly on KolabNow (to be changed) - Only synchronization, not adding / changing / removing WebDAV collections or items (to be implemented) - Only events are currently supported (todo, freebusy, etc. are to be implemented but should be straightforward) Fixes T8224 Reviewers: cmollekopf Tags: #sink Maniphest Tasks: T8224 Differential Revision: https://phabricator.kde.org/D11741
-rw-r--r--common/CMakeLists.txt1
-rw-r--r--common/domain/applicationdomaintype.cpp10
-rw-r--r--common/domain/applicationdomaintype.h24
-rw-r--r--common/domain/applicationdomaintype_p.h2
-rw-r--r--common/domain/calendar.fbs8
-rw-r--r--common/domain/event.fbs3
-rw-r--r--common/domain/typeimplementations.cpp25
-rw-r--r--common/domain/typeimplementations.h12
-rw-r--r--examples/CMakeLists.txt4
-rw-r--r--examples/caldavresource/CMakeLists.txt15
-rw-r--r--examples/caldavresource/caldavresource.cpp155
-rw-r--r--examples/caldavresource/caldavresource.h46
-rw-r--r--examples/caldavresource/tests/CMakeLists.txt9
-rw-r--r--examples/caldavresource/tests/caldavtest.cpp93
-rw-r--r--examples/carddavresource/CMakeLists.txt (renamed from examples/davresource/CMakeLists.txt)6
-rw-r--r--examples/carddavresource/carddavresource.cpp147
-rw-r--r--examples/carddavresource/carddavresource.h (renamed from examples/davresource/davresource.h)16
-rw-r--r--examples/davresource/davresource.cpp315
-rw-r--r--examples/dummyresource/resourcefactory.cpp2
-rw-r--r--examples/webdavcommon/CMakeLists.txt8
-rw-r--r--examples/webdavcommon/webdav.cpp277
-rw-r--r--examples/webdavcommon/webdav.h78
-rw-r--r--tests/CMakeLists.txt1
-rw-r--r--tests/calendar.fbs12
-rw-r--r--tests/domainadaptortest.cpp2
-rw-r--r--tests/storagebenchmark.cpp11
26 files changed, 922 insertions, 360 deletions
diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt
index 76579dd..9c4d4f1 100644
--- a/common/CMakeLists.txt
+++ b/common/CMakeLists.txt
@@ -104,6 +104,7 @@ generate_flatbuffers(
104 domain/contact 104 domain/contact
105 domain/addressbook 105 domain/addressbook
106 domain/event 106 domain/event
107 domain/calendar
107 domain/mail 108 domain/mail
108 domain/folder 109 domain/folder
109 domain/dummy 110 domain/dummy
diff --git a/common/domain/applicationdomaintype.cpp b/common/domain/applicationdomaintype.cpp
index ff2990d..9a213dd 100644
--- a/common/domain/applicationdomaintype.cpp
+++ b/common/domain/applicationdomaintype.cpp
@@ -395,7 +395,15 @@ SinkResource ImapResource::create(const QByteArray &account)
395SinkResource CardDavResource::create(const QByteArray &account) 395SinkResource CardDavResource::create(const QByteArray &account)
396{ 396{
397 auto &&resource = ApplicationDomainType::createEntity<SinkResource>(); 397 auto &&resource = ApplicationDomainType::createEntity<SinkResource>();
398 resource.setResourceType("sink.dav"); 398 resource.setResourceType("sink.carddav");
399 resource.setAccount(account);
400 return resource;
401}
402
403SinkResource CalDavResource::create(const QByteArray &account)
404{
405 auto &&resource = ApplicationDomainType::createEntity<SinkResource>();
406 resource.setResourceType("sink.caldav");
399 resource.setAccount(account); 407 resource.setAccount(account);
400 return resource; 408 return resource;
401} 409}
diff --git a/common/domain/applicationdomaintype.h b/common/domain/applicationdomaintype.h
index d05e981..e05acaa 100644
--- a/common/domain/applicationdomaintype.h
+++ b/common/domain/applicationdomaintype.h
@@ -389,22 +389,24 @@ struct SINK_EXPORT Contact : public Entity {
389 SINK_REFERENCE_PROPERTY(Addressbook, Addressbook, addressbook); 389 SINK_REFERENCE_PROPERTY(Addressbook, Addressbook, addressbook);
390}; 390};
391 391
392struct SINK_EXPORT Calendar : public Entity {
393 SINK_ENTITY(Calendar, calendar);
394 SINK_PROPERTY(QString, Name, name);
395};
396
392struct SINK_EXPORT Event : public Entity { 397struct SINK_EXPORT Event : public Entity {
393 SINK_ENTITY(Event, event); 398 SINK_ENTITY(Event, event);
394 SINK_PROPERTY(QString, Uid, uid); 399 SINK_PROPERTY(QString, Uid, uid);
395 SINK_PROPERTY(QString, Summary, summary); 400 SINK_PROPERTY(QString, Summary, summary);
396 SINK_PROPERTY(QString, Description, description); 401 SINK_PROPERTY(QString, Description, description);
397 SINK_PROPERTY(QByteArray, Attachment, attachment); 402 SINK_PROPERTY(QDateTime, StartTime, startTime);
403 SINK_REFERENCE_PROPERTY(Calendar, Calendar, calendar);
398}; 404};
399 405
400struct SINK_EXPORT Todo : public Entity { 406struct SINK_EXPORT Todo : public Entity {
401 SINK_ENTITY(Todo, todo); 407 SINK_ENTITY(Todo, todo);
402}; 408};
403 409
404struct SINK_EXPORT Calendar : public Entity {
405 SINK_ENTITY(Calendar, calendar);
406};
407
408struct SINK_EXPORT Folder : public Entity { 410struct SINK_EXPORT Folder : public Entity {
409 SINK_ENTITY(Folder, folder); 411 SINK_ENTITY(Folder, folder);
410 SINK_REFERENCE_PROPERTY(Folder, Parent, parent); 412 SINK_REFERENCE_PROPERTY(Folder, Parent, parent);
@@ -477,6 +479,10 @@ struct SINK_EXPORT CardDavResource {
477 static SinkResource create(const QByteArray &account); 479 static SinkResource create(const QByteArray &account);
478}; 480};
479 481
482struct SINK_EXPORT CalDavResource {
483 static SinkResource create(const QByteArray &account);
484};
485
480namespace ResourceCapabilities { 486namespace ResourceCapabilities {
481namespace Mail { 487namespace Mail {
482 static constexpr const char *mail = "mail"; 488 static constexpr const char *mail = "mail";
@@ -493,6 +499,11 @@ namespace Contact {
493 static constexpr const char *addressbook = "addressbook"; 499 static constexpr const char *addressbook = "addressbook";
494 static constexpr const char *storage = "contact.storage"; 500 static constexpr const char *storage = "contact.storage";
495}; 501};
502namespace Event {
503 static constexpr const char *event = "event";
504 static constexpr const char *calendar = "calendar";
505 static constexpr const char *storage = "event.storage";
506};
496}; 507};
497 508
498namespace SpecialPurpose { 509namespace SpecialPurpose {
@@ -522,7 +533,7 @@ bool SINK_EXPORT isGlobalType(const QByteArray &type);
522 533
523/** 534/**
524 * Type implementation. 535 * Type implementation.
525 * 536 *
526 * Needs to be implemented for every application domain type. 537 * Needs to be implemented for every application domain type.
527 * Contains all non-resource specific, but type-specific code. 538 * Contains all non-resource specific, but type-specific code.
528 */ 539 */
@@ -545,6 +556,7 @@ class SINK_EXPORT TypeImplementation;
545 REGISTER_TYPE(Sink::ApplicationDomain::Contact) \ 556 REGISTER_TYPE(Sink::ApplicationDomain::Contact) \
546 REGISTER_TYPE(Sink::ApplicationDomain::Addressbook) \ 557 REGISTER_TYPE(Sink::ApplicationDomain::Addressbook) \
547 REGISTER_TYPE(Sink::ApplicationDomain::Event) \ 558 REGISTER_TYPE(Sink::ApplicationDomain::Event) \
559 REGISTER_TYPE(Sink::ApplicationDomain::Calendar) \
548 REGISTER_TYPE(Sink::ApplicationDomain::Mail) \ 560 REGISTER_TYPE(Sink::ApplicationDomain::Mail) \
549 REGISTER_TYPE(Sink::ApplicationDomain::Folder) \ 561 REGISTER_TYPE(Sink::ApplicationDomain::Folder) \
550 REGISTER_TYPE(Sink::ApplicationDomain::SinkResource) \ 562 REGISTER_TYPE(Sink::ApplicationDomain::SinkResource) \
diff --git a/common/domain/applicationdomaintype_p.h b/common/domain/applicationdomaintype_p.h
index a60df38..734ac3e 100644
--- a/common/domain/applicationdomaintype_p.h
+++ b/common/domain/applicationdomaintype_p.h
@@ -38,6 +38,8 @@ struct TypeHelper {
38 return Func<Sink::ApplicationDomain::Mail>{}(std::forward<Args...>(args...)); 38 return Func<Sink::ApplicationDomain::Mail>{}(std::forward<Args...>(args...));
39 } else if (type == Sink::ApplicationDomain::getTypeName<Sink::ApplicationDomain::Event>()) { 39 } else if (type == Sink::ApplicationDomain::getTypeName<Sink::ApplicationDomain::Event>()) {
40 return Func<Sink::ApplicationDomain::Event>{}(std::forward<Args...>(args...)); 40 return Func<Sink::ApplicationDomain::Event>{}(std::forward<Args...>(args...));
41 } else if (type == Sink::ApplicationDomain::getTypeName<Sink::ApplicationDomain::Calendar>()) {
42 return Func<Sink::ApplicationDomain::Calendar>{}(std::forward<Args...>(args...));
41 } else if (type == Sink::ApplicationDomain::getTypeName<Sink::ApplicationDomain::Contact>()) { 43 } else if (type == Sink::ApplicationDomain::getTypeName<Sink::ApplicationDomain::Contact>()) {
42 return Func<Sink::ApplicationDomain::Contact>{}(std::forward<Args...>(args...)); 44 return Func<Sink::ApplicationDomain::Contact>{}(std::forward<Args...>(args...));
43 } else if (type == Sink::ApplicationDomain::getTypeName<Sink::ApplicationDomain::Addressbook>()) { 45 } else if (type == Sink::ApplicationDomain::getTypeName<Sink::ApplicationDomain::Addressbook>()) {
diff --git a/common/domain/calendar.fbs b/common/domain/calendar.fbs
new file mode 100644
index 0000000..9788539
--- /dev/null
+++ b/common/domain/calendar.fbs
@@ -0,0 +1,8 @@
1namespace Sink.ApplicationDomain.Buffer;
2
3table Calendar {
4 name:string;
5}
6
7root_type Calendar;
8file_identifier "AKFB";
diff --git a/common/domain/event.fbs b/common/domain/event.fbs
index 69148ef..68c8608 100644
--- a/common/domain/event.fbs
+++ b/common/domain/event.fbs
@@ -4,7 +4,8 @@ table Event {
4 uid:string; 4 uid:string;
5 summary:string; 5 summary:string;
6 description:string; 6 description:string;
7 attachment:[ubyte]; 7 startTime:string;
8 calendar:string;
8} 9}
9 10
10root_type Event; 11root_type Event;
diff --git a/common/domain/typeimplementations.cpp b/common/domain/typeimplementations.cpp
index 29da7ea..fe70d74 100644
--- a/common/domain/typeimplementations.cpp
+++ b/common/domain/typeimplementations.cpp
@@ -67,6 +67,11 @@ typedef IndexConfig<Event,
67 ValueIndex<Event::Uid> 67 ValueIndex<Event::Uid>
68 > EventIndexConfig; 68 > EventIndexConfig;
69 69
70typedef IndexConfig<Calendar,
71 ValueIndex<Calendar::Name>
72 > CalendarIndexConfig;
73
74
70 75
71void TypeImplementation<Mail>::configure(TypeIndex &index) 76void TypeImplementation<Mail>::configure(TypeIndex &index)
72{ 77{
@@ -201,7 +206,8 @@ void TypeImplementation<Event>::configure(PropertyMapper &propertyMapper)
201 SINK_REGISTER_SERIALIZER(propertyMapper, Event, Summary, summary); 206 SINK_REGISTER_SERIALIZER(propertyMapper, Event, Summary, summary);
202 SINK_REGISTER_SERIALIZER(propertyMapper, Event, Description, description); 207 SINK_REGISTER_SERIALIZER(propertyMapper, Event, Description, description);
203 SINK_REGISTER_SERIALIZER(propertyMapper, Event, Uid, uid); 208 SINK_REGISTER_SERIALIZER(propertyMapper, Event, Uid, uid);
204 SINK_REGISTER_SERIALIZER(propertyMapper, Event, Attachment, attachment); 209 SINK_REGISTER_SERIALIZER(propertyMapper, Event, StartTime, startTime);
210 SINK_REGISTER_SERIALIZER(propertyMapper, Event, Calendar, calendar);
205} 211}
206 212
207void TypeImplementation<Event>::configure(IndexPropertyMapper &) 213void TypeImplementation<Event>::configure(IndexPropertyMapper &)
@@ -209,3 +215,20 @@ void TypeImplementation<Event>::configure(IndexPropertyMapper &)
209 215
210} 216}
211 217
218
219void TypeImplementation<Calendar>::configure(TypeIndex &index)
220{
221 CalendarIndexConfig::configure(index);
222}
223
224QMap<QByteArray, int> TypeImplementation<Calendar>::typeDatabases()
225{
226 return merge(QMap<QByteArray, int>{{QByteArray{Calendar::name} + ".main", 0}}, CalendarIndexConfig::databases());
227}
228
229void TypeImplementation<Calendar>::configure(PropertyMapper &propertyMapper)
230{
231 SINK_REGISTER_SERIALIZER(propertyMapper, Calendar, Name, name);
232}
233
234void TypeImplementation<Calendar>::configure(IndexPropertyMapper &) {}
diff --git a/common/domain/typeimplementations.h b/common/domain/typeimplementations.h
index d36dfc1..7a8a602 100644
--- a/common/domain/typeimplementations.h
+++ b/common/domain/typeimplementations.h
@@ -23,6 +23,7 @@
23#include "mail_generated.h" 23#include "mail_generated.h"
24#include "folder_generated.h" 24#include "folder_generated.h"
25#include "event_generated.h" 25#include "event_generated.h"
26#include "calendar_generated.h"
26#include "contact_generated.h" 27#include "contact_generated.h"
27#include "addressbook_generated.h" 28#include "addressbook_generated.h"
28 29
@@ -94,5 +95,16 @@ public:
94 static QMap<QByteArray, int> typeDatabases(); 95 static QMap<QByteArray, int> typeDatabases();
95}; 96};
96 97
98template<>
99class TypeImplementation<Sink::ApplicationDomain::Calendar> {
100public:
101 typedef Sink::ApplicationDomain::Buffer::Calendar Buffer;
102 typedef Sink::ApplicationDomain::Buffer::CalendarBuilder BufferBuilder;
103 static void configure(TypeIndex &);
104 static void configure(PropertyMapper &);
105 static void configure(IndexPropertyMapper &indexPropertyMapper);
106 static QMap<QByteArray, int> typeDatabases();
107};
108
97} 109}
98} 110}
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index adfb5e1..6de489a 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -9,5 +9,7 @@ if (BUILD_MAILDIR)
9endif() 9endif()
10add_subdirectory(mailtransportresource) 10add_subdirectory(mailtransportresource)
11if (BUILD_DAV) 11if (BUILD_DAV)
12 add_subdirectory(davresource) 12 add_subdirectory(webdavcommon)
13 add_subdirectory(carddavresource)
14 add_subdirectory(caldavresource)
13endif() 15endif()
diff --git a/examples/caldavresource/CMakeLists.txt b/examples/caldavresource/CMakeLists.txt
new file mode 100644
index 0000000..0057e8b
--- /dev/null
+++ b/examples/caldavresource/CMakeLists.txt
@@ -0,0 +1,15 @@
1project(sink_resource_caldav)
2
3add_definitions(-DQT_PLUGIN)
4include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
5
6find_package(KPimKDAV2 REQUIRED)
7find_package(KF5CalendarCore REQUIRED)
8
9add_library(${PROJECT_NAME} SHARED caldavresource.cpp)
10target_link_libraries(${PROJECT_NAME} sink_webdav_common sink Qt5::Core Qt5::Network KPim::KDAV2
11 KF5::CalendarCore)
12
13install(TARGETS ${PROJECT_NAME} LIBRARY DESTINATION ${SINK_RESOURCE_PLUGINS_PATH})
14
15add_subdirectory(tests)
diff --git a/examples/caldavresource/caldavresource.cpp b/examples/caldavresource/caldavresource.cpp
new file mode 100644
index 0000000..2bcdfa1
--- /dev/null
+++ b/examples/caldavresource/caldavresource.cpp
@@ -0,0 +1,155 @@
1/*
2 * Copyright (C) 2018 Christian Mollekopf <chrigi_1@fastmail.fm>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20#include "caldavresource.h"
21
22#include "../webdavcommon/webdav.h"
23
24#include "adaptorfactoryregistry.h"
25#include "applicationdomaintype.h"
26#include "domainadaptor.h"
27#include "facade.h"
28#include "facadefactory.h"
29
30#include <KCalCore/ICalFormat>
31
32#define ENTITY_TYPE_EVENT "event"
33#define ENTITY_TYPE_CALENDAR "calendar"
34
35using Sink::ApplicationDomain::getTypeName;
36
37class EventSynchronizer : public WebDavSynchronizer
38{
39 using Event = Sink::ApplicationDomain::Event;
40 using Calendar = Sink::ApplicationDomain::Calendar;
41
42public:
43 explicit EventSynchronizer(const Sink::ResourceContext &context)
44 : WebDavSynchronizer(context, KDAV2::CalDav, getTypeName<Calendar>(), getTypeName<Event>())
45 {}
46
47protected:
48 void updateLocalCollections(KDAV2::DavCollection::List calendarList) Q_DECL_OVERRIDE
49 {
50 SinkLog() << "Found" << calendarList.size() << "calendar(s)";
51
52 QVector<QByteArray> ridList;
53 for (const auto &remoteCalendar : calendarList) {
54 const auto &rid = resourceID(remoteCalendar);
55 SinkLog() << "Found calendar:" << remoteCalendar.displayName() << "[" << rid << "]";
56
57 Calendar localCalendar;
58 localCalendar.setName(remoteCalendar.displayName());
59
60 createOrModify(ENTITY_TYPE_CALENDAR, rid, localCalendar,
61 /* mergeCriteria = */ QHash<QByteArray, Sink::Query::Comparator>{});
62 }
63 }
64
65 void updateLocalItem(KDAV2::DavItem remoteItem, const QByteArray &calendarLocalId) Q_DECL_OVERRIDE
66 {
67 const auto &rid = resourceID(remoteItem);
68
69 auto incidence = KCalCore::ICalFormat().fromString(remoteItem.data());
70
71 using Type = KCalCore::IncidenceBase::IncidenceType;
72
73 switch (incidence->type()) {
74 case Type::TypeEvent: {
75 auto remoteEvent = dynamic_cast<const KCalCore::Event &>(*incidence);
76
77 Event localEvent;
78 localEvent.setUid(remoteEvent.uid());
79 localEvent.setSummary(remoteEvent.summary());
80 localEvent.setDescription(remoteEvent.description());
81 localEvent.setStartTime(remoteEvent.dtStart());
82 localEvent.setCalendar(calendarLocalId);
83
84 SinkTrace() << "Found an event:" << localEvent.getSummary() << "with id:" << rid;
85
86 createOrModify(ENTITY_TYPE_EVENT, rid, localEvent,
87 /* mergeCriteria = */ QHash<QByteArray, Sink::Query::Comparator>{});
88 break;
89 }
90 case Type::TypeTodo:
91 SinkWarning() << "Unimplemented add of a 'Todo' item in the Store";
92 break;
93 case Type::TypeJournal:
94 SinkWarning() << "Unimplemented add of a 'Journal' item in the Store";
95 break;
96 case Type::TypeFreeBusy:
97 SinkWarning() << "Unimplemented add of a 'FreeBusy' item in the Store";
98 break;
99 case Type::TypeUnknown:
100 SinkWarning() << "Trying to add a 'Unknown' item";
101 break;
102 default:
103 break;
104 }
105 }
106
107 QByteArray collectionLocalResourceID(const KDAV2::DavCollection &calendar) Q_DECL_OVERRIDE
108 {
109 return syncStore().resolveRemoteId(ENTITY_TYPE_CALENDAR, resourceID(calendar));
110 }
111};
112
113CalDavResource::CalDavResource(const Sink::ResourceContext &context)
114 : Sink::GenericResource(context)
115{
116 auto synchronizer = QSharedPointer<EventSynchronizer>::create(context);
117 setupSynchronizer(synchronizer);
118
119 // setupPreprocessors(ENTITY_TYPE_EVENT, QVector<Sink::Preprocessor*>() << new EventPropertyExtractor);
120}
121
122CalDavResourceFactory::CalDavResourceFactory(QObject *parent)
123 : Sink::ResourceFactory(parent, {
124 Sink::ApplicationDomain::ResourceCapabilities::Event::event,
125 Sink::ApplicationDomain::ResourceCapabilities::Event::calendar,
126 Sink::ApplicationDomain::ResourceCapabilities::Event::storage,
127 })
128{}
129
130Sink::Resource *CalDavResourceFactory::createResource(const Sink::ResourceContext &context)
131{
132 return new CalDavResource(context);
133}
134
135using Sink::ApplicationDomain::Calendar;
136using Sink::ApplicationDomain::Event;
137
138void CalDavResourceFactory::registerFacades(const QByteArray &resourceName, Sink::FacadeFactory &factory)
139{
140 factory.registerFacade<Event, Sink::DefaultFacade<Event>>(resourceName);
141 factory.registerFacade<Calendar, Sink::DefaultFacade<Calendar>>(resourceName);
142}
143
144
145void CalDavResourceFactory::registerAdaptorFactories(
146 const QByteArray &resourceName, Sink::AdaptorFactoryRegistry &registry)
147{
148 registry.registerFactory<Event, DefaultAdaptorFactory<Event>>(resourceName);
149 registry.registerFactory<Calendar, DefaultAdaptorFactory<Calendar>>(resourceName);
150}
151
152void CalDavResourceFactory::removeDataFromDisk(const QByteArray &instanceIdentifier)
153{
154 CalDavResource::removeFromDisk(instanceIdentifier);
155}
diff --git a/examples/caldavresource/caldavresource.h b/examples/caldavresource/caldavresource.h
new file mode 100644
index 0000000..5822495
--- /dev/null
+++ b/examples/caldavresource/caldavresource.h
@@ -0,0 +1,46 @@
1/*
2 * Copyright (C) 2018 Christian Mollekopf <chrigi_1@fastmail.fm>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20#pragma once
21
22#include "common/genericresource.h"
23
24/**
25 * A CalDAV resource.
26 */
27class CalDavResource : public Sink::GenericResource
28{
29public:
30 CalDavResource(const Sink::ResourceContext &);
31};
32
33class CalDavResourceFactory : public Sink::ResourceFactory
34{
35 Q_OBJECT
36 Q_PLUGIN_METADATA(IID "sink.caldav")
37 Q_INTERFACES(Sink::ResourceFactory)
38
39public:
40 CalDavResourceFactory(QObject *parent = nullptr);
41
42 Sink::Resource *createResource(const Sink::ResourceContext &context) Q_DECL_OVERRIDE;
43 void registerFacades(const QByteArray &resourceName, Sink::FacadeFactory &factory) Q_DECL_OVERRIDE;
44 void registerAdaptorFactories(const QByteArray &resourceName, Sink::AdaptorFactoryRegistry &registry) Q_DECL_OVERRIDE;
45 void removeDataFromDisk(const QByteArray &instanceIdentifier) Q_DECL_OVERRIDE;
46};
diff --git a/examples/caldavresource/tests/CMakeLists.txt b/examples/caldavresource/tests/CMakeLists.txt
new file mode 100644
index 0000000..d2f9b50
--- /dev/null
+++ b/examples/caldavresource/tests/CMakeLists.txt
@@ -0,0 +1,9 @@
1set(CMAKE_AUTOMOC ON)
2include_directories(${CMAKE_BINARY_DIR})
3
4include(SinkTest)
5
6auto_tests (
7 caldavtest
8)
9target_link_libraries(caldavtest sink_resource_caldav)
diff --git a/examples/caldavresource/tests/caldavtest.cpp b/examples/caldavresource/tests/caldavtest.cpp
new file mode 100644
index 0000000..f999590
--- /dev/null
+++ b/examples/caldavresource/tests/caldavtest.cpp
@@ -0,0 +1,93 @@
1#include <QtTest>
2
3#include "../caldavresource.h"
4
5#include "common/resourcecontrol.h"
6#include "common/secretstore.h"
7#include "common/store.h"
8#include "common/test.h"
9#include "tests/testutils.h"
10
11using Sink::ApplicationDomain::Calendar;
12using Sink::ApplicationDomain::DummyResource;
13using Sink::ApplicationDomain::Event;
14using Sink::ApplicationDomain::SinkResource;
15
16class CalDavTest : public QObject
17{
18 Q_OBJECT
19
20 SinkResource createResource()
21 {
22 auto resource = Sink::ApplicationDomain::CalDavResource::create("account1");
23 resource.setProperty("server", "http://localhost/dav/calendars/users/doe");
24 resource.setProperty("username", "doe");
25 Sink::SecretStore::instance().insert(resource.identifier(), "doe");
26 resource.setProperty("testmode", true);
27 return resource;
28 }
29
30
31 QByteArray mResourceInstanceIdentifier;
32 QByteArray mStorageResource;
33
34private slots:
35
36 void initTestCase()
37 {
38 Sink::Test::initTest();
39 auto resource = createResource();
40 QVERIFY(!resource.identifier().isEmpty());
41 VERIFYEXEC(Sink::Store::create(resource));
42 mResourceInstanceIdentifier = resource.identifier();
43
44 auto dummyResource = DummyResource::create("account1");
45 VERIFYEXEC(Sink::Store::create(dummyResource));
46 mStorageResource = dummyResource.identifier();
47 QVERIFY(!mStorageResource.isEmpty());
48 }
49
50 void cleanup()
51 {
52 VERIFYEXEC(Sink::Store::removeDataFromDisk(mResourceInstanceIdentifier));
53 VERIFYEXEC(Sink::Store::removeDataFromDisk(mStorageResource));
54 }
55
56 void init()
57 {
58 VERIFYEXEC(Sink::ResourceControl::start(mResourceInstanceIdentifier));
59 }
60
61 void testSyncCal()
62 {
63 VERIFYEXEC(Sink::Store::synchronize(Sink::Query().resourceFilter(mResourceInstanceIdentifier)));
64 // Check in the logs that it doesn't synchronize events again because same CTag
65 VERIFYEXEC(Sink::Store::synchronize(Sink::Query().resourceFilter(mResourceInstanceIdentifier)));
66 }
67
68 void testSyncCalEmpty()
69 {
70 VERIFYEXEC(Sink::Store::synchronize(Sink::Query().resourceFilter(mResourceInstanceIdentifier)));
71
72 auto eventJob =
73 Sink::Store::fetchAll<Event>(Sink::Query().request<Event::Uid>()).then([](const QList<Event::Ptr> &events) {
74 QCOMPARE(events.size(), 14);
75 });
76 VERIFYEXEC(eventJob);
77
78 auto calendarJob =
79 Sink::Store::fetchAll<Calendar>(Sink::Query().request<Calendar::Name>()).then([](const QList<Calendar::Ptr> &calendars) {
80 QCOMPARE(calendars.size(), 2);
81 for (const auto &calendar : calendars) {
82 QVERIFY(calendar->getName() == "Calendar" || calendar->getName() == "Tasks");
83 }
84 });
85 VERIFYEXEC(calendarJob);
86
87 SinkLog() << "Finished";
88 }
89};
90
91QTEST_MAIN(CalDavTest)
92
93#include "caldavtest.moc"
diff --git a/examples/davresource/CMakeLists.txt b/examples/carddavresource/CMakeLists.txt
index 2351ecd..2c69d26 100644
--- a/examples/davresource/CMakeLists.txt
+++ b/examples/carddavresource/CMakeLists.txt
@@ -1,11 +1,11 @@
1project(sink_resource_dav) 1project(sink_resource_carddav)
2 2
3add_definitions(-DQT_PLUGIN) 3add_definitions(-DQT_PLUGIN)
4include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) 4include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
5 5
6find_package(KPimKDAV2 REQUIRED) 6find_package(KPimKDAV2 REQUIRED)
7 7
8add_library(${PROJECT_NAME} SHARED davresource.cpp) 8add_library(${PROJECT_NAME} SHARED carddavresource.cpp)
9target_link_libraries(${PROJECT_NAME} sink Qt5::Core Qt5::Network KPim::KDAV2) 9target_link_libraries(${PROJECT_NAME} sink_webdav_common sink Qt5::Core Qt5::Network KPim::KDAV2)
10 10
11install(TARGETS ${PROJECT_NAME} LIBRARY DESTINATION ${SINK_RESOURCE_PLUGINS_PATH}) 11install(TARGETS ${PROJECT_NAME} LIBRARY DESTINATION ${SINK_RESOURCE_PLUGINS_PATH})
diff --git a/examples/carddavresource/carddavresource.cpp b/examples/carddavresource/carddavresource.cpp
new file mode 100644
index 0000000..fc2b946
--- /dev/null
+++ b/examples/carddavresource/carddavresource.cpp
@@ -0,0 +1,147 @@
1/*
2 * Copyright (C) 2015 Christian Mollekopf <chrigi_1@fastmail.fm>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20#include "carddavresource.h"
21
22#include "../webdavcommon/webdav.h"
23
24#include "facade.h"
25#include "resourceconfig.h"
26#include "log.h"
27#include "definitions.h"
28#include "synchronizer.h"
29#include "inspector.h"
30
31#include "facadefactory.h"
32#include "adaptorfactoryregistry.h"
33
34#include "contactpreprocessor.h"
35
36//This is the resources entity type, and not the domain type
37#define ENTITY_TYPE_CONTACT "contact"
38#define ENTITY_TYPE_ADDRESSBOOK "addressbook"
39
40using namespace Sink;
41
42class ContactSynchronizer : public WebDavSynchronizer
43{
44public:
45 ContactSynchronizer(const Sink::ResourceContext &resourceContext)
46 : WebDavSynchronizer(resourceContext, KDAV2::CardDav,
47 ApplicationDomain::getTypeName<ApplicationDomain::Addressbook>(),
48 ApplicationDomain::getTypeName<ApplicationDomain::Contact>())
49 {}
50 QByteArray createAddressbook(const QString &addressbookName, const QString &addressbookPath, const QString &parentAddressbookRid)
51 {
52 SinkTrace() << "Creating addressbook: " << addressbookName << parentAddressbookRid;
53 const auto remoteId = addressbookPath.toUtf8();
54 const auto bufferType = ENTITY_TYPE_ADDRESSBOOK;
55 Sink::ApplicationDomain::Addressbook addressbook;
56 addressbook.setName(addressbookName);
57 QHash<QByteArray, Query::Comparator> mergeCriteria;
58
59 if (!parentAddressbookRid.isEmpty()) {
60 addressbook.setParent(syncStore().resolveRemoteId(ENTITY_TYPE_ADDRESSBOOK, parentAddressbookRid.toUtf8()));
61 }
62 createOrModify(bufferType, remoteId, addressbook, mergeCriteria);
63 return remoteId;
64 }
65
66protected:
67 void updateLocalCollections(KDAV2::DavCollection::List addressbookList) Q_DECL_OVERRIDE
68 {
69 const QByteArray bufferType = ENTITY_TYPE_ADDRESSBOOK;
70 SinkTrace() << "Found" << addressbookList.size() << "addressbooks";
71
72 for (const auto &f : addressbookList) {
73 const auto &rid = resourceID(f);
74 SinkLog() << "Found addressbook:" << rid << f.displayName();
75 createAddressbook(f.displayName(), rid, "");
76 }
77 }
78
79 void updateLocalItem(KDAV2::DavItem remoteContact, const QByteArray &addressbookLocalId) Q_DECL_OVERRIDE
80 {
81 Sink::ApplicationDomain::Contact localContact;
82
83 localContact.setVcard(remoteContact.data());
84 localContact.setAddressbook(addressbookLocalId);
85
86 QHash<QByteArray, Query::Comparator> mergeCriteria;
87 createOrModify(ENTITY_TYPE_CONTACT, resourceID(remoteContact), localContact, mergeCriteria);
88 }
89
90 QByteArray collectionLocalResourceID(const KDAV2::DavCollection &addressbook) Q_DECL_OVERRIDE
91 {
92 return syncStore().resolveRemoteId(ENTITY_TYPE_ADDRESSBOOK, resourceID(addressbook));
93 }
94
95 KAsync::Job<QByteArray> replay(const ApplicationDomain::Contact &contact, Sink::Operation operation, const QByteArray &oldRemoteId, const QList<QByteArray> &changedProperties) Q_DECL_OVERRIDE
96 {
97 return KAsync::null<QByteArray>();
98 }
99
100 KAsync::Job<QByteArray> replay(const ApplicationDomain::Addressbook &addressbook, Sink::Operation operation, const QByteArray &oldRemoteId, const QList<QByteArray> &changedProperties) Q_DECL_OVERRIDE
101 {
102 return KAsync::null<QByteArray>();
103 }
104};
105
106
107CardDavResource::CardDavResource(const Sink::ResourceContext &resourceContext)
108 : Sink::GenericResource(resourceContext)
109{
110 auto synchronizer = QSharedPointer<ContactSynchronizer>::create(resourceContext);
111 setupSynchronizer(synchronizer);
112
113 setupPreprocessors(ENTITY_TYPE_CONTACT, QVector<Sink::Preprocessor*>() << new ContactPropertyExtractor);
114}
115
116
117CardDavResourceFactory::CardDavResourceFactory(QObject *parent)
118 : Sink::ResourceFactory(parent,
119 {Sink::ApplicationDomain::ResourceCapabilities::Contact::contact,
120 Sink::ApplicationDomain::ResourceCapabilities::Contact::addressbook,
121 Sink::ApplicationDomain::ResourceCapabilities::Contact::storage
122 }
123 )
124{
125}
126
127Sink::Resource *CardDavResourceFactory::createResource(const ResourceContext &context)
128{
129 return new CardDavResource(context);
130}
131
132void CardDavResourceFactory::registerFacades(const QByteArray &name, Sink::FacadeFactory &factory)
133{
134 factory.registerFacade<ApplicationDomain::Contact, DefaultFacade<ApplicationDomain::Contact>>(name);
135 factory.registerFacade<ApplicationDomain::Addressbook, DefaultFacade<ApplicationDomain::Addressbook>>(name);
136}
137
138void CardDavResourceFactory::registerAdaptorFactories(const QByteArray &name, Sink::AdaptorFactoryRegistry &registry)
139{
140 registry.registerFactory<ApplicationDomain::Contact, DefaultAdaptorFactory<ApplicationDomain::Contact>>(name);
141 registry.registerFactory<ApplicationDomain::Addressbook, DefaultAdaptorFactory<ApplicationDomain::Addressbook>>(name);
142}
143
144void CardDavResourceFactory::removeDataFromDisk(const QByteArray &instanceIdentifier)
145{
146 CardDavResource::removeFromDisk(instanceIdentifier);
147}
diff --git a/examples/davresource/davresource.h b/examples/carddavresource/carddavresource.h
index b4f9e5a..3c0f707 100644
--- a/examples/davresource/davresource.h
+++ b/examples/carddavresource/carddavresource.h
@@ -30,30 +30,30 @@ class ContactAdaptorFactory;
30class AddressbookAdaptorFactory; 30class AddressbookAdaptorFactory;
31 31
32/** 32/**
33 * A DAV resource. 33 * A CardDAV resource.
34 * 34 *
35 * Implementation details: 35 * Implementation details:
36 * The remoteid's have the following formats: 36 * The remoteid's have the following formats:
37 * files: full file path 37 * files: full file path
38 * directories: full directory path 38 * directories: full directory path
39 * 39 *
40 * The resource moves all messages from new to cur during sync and thus expectes all messages that are in the store to always reside in cur. 40 * The resource moves all messages from new to cur during sync and thus expectes all messages that are in the store to always reside in cur.
41 * The tmp directory is never directly used 41 * The tmp directory is never directly used
42 */ 42 */
43class DavResource : public Sink::GenericResource 43class CardDavResource : public Sink::GenericResource
44{ 44{
45public: 45public:
46 DavResource(const Sink::ResourceContext &resourceContext); 46 CardDavResource(const Sink::ResourceContext &resourceContext);
47}; 47};
48 48
49class DavResourceFactory : public Sink::ResourceFactory 49class CardDavResourceFactory : public Sink::ResourceFactory
50{ 50{
51 Q_OBJECT 51 Q_OBJECT
52 Q_PLUGIN_METADATA(IID "sink.dav") 52 Q_PLUGIN_METADATA(IID "sink.carddav")
53 Q_INTERFACES(Sink::ResourceFactory) 53 Q_INTERFACES(Sink::ResourceFactory)
54 54
55public: 55public:
56 DavResourceFactory(QObject *parent = 0); 56 CardDavResourceFactory(QObject *parent = 0);
57 57
58 Sink::Resource *createResource(const Sink::ResourceContext &context) Q_DECL_OVERRIDE; 58 Sink::Resource *createResource(const Sink::ResourceContext &context) Q_DECL_OVERRIDE;
59 void registerFacades(const QByteArray &resourceName, Sink::FacadeFactory &factory) Q_DECL_OVERRIDE; 59 void registerFacades(const QByteArray &resourceName, Sink::FacadeFactory &factory) Q_DECL_OVERRIDE;
diff --git a/examples/davresource/davresource.cpp b/examples/davresource/davresource.cpp
deleted file mode 100644
index fde7055..0000000
--- a/examples/davresource/davresource.cpp
+++ /dev/null
@@ -1,315 +0,0 @@
1/*
2 * Copyright (C) 2015 Christian Mollekopf <chrigi_1@fastmail.fm>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20#include "davresource.h"
21
22#include "facade.h"
23#include "resourceconfig.h"
24#include "log.h"
25#include "definitions.h"
26#include "synchronizer.h"
27#include "inspector.h"
28
29#include "facadefactory.h"
30#include "adaptorfactoryregistry.h"
31
32#include "contactpreprocessor.h"
33
34#include <QNetworkReply>
35#include <KDAV2/DavCollection>
36#include <KDAV2/DavCollectionsFetchJob>
37#include <KDAV2/DavItem>
38#include <KDAV2/DavItemsListJob>
39#include <KDAV2/DavItemFetchJob>
40#include <KDAV2/EtagCache>
41#include <KDAV2/DavJobBase>
42
43//This is the resources entity type, and not the domain type
44#define ENTITY_TYPE_CONTACT "contact"
45#define ENTITY_TYPE_ADDRESSBOOK "addressbook"
46
47using namespace Sink;
48
49static int translateDavError(KJob *job)
50{
51 const int responseCode = static_cast<KDAV2::DavJobBase*>(job)->latestResponseCode();
52
53 switch (responseCode) {
54 case QNetworkReply::HostNotFoundError:
55 return ApplicationDomain::NoServerError;
56 //Since we don't login we will just not have the necessary permissions ot view the object
57 case QNetworkReply::OperationCanceledError:
58 return ApplicationDomain::LoginError;
59 }
60 return ApplicationDomain::UnknownError;
61}
62
63static KAsync::Job<void> runJob(KJob *job)
64{
65 return KAsync::start<void>([job](KAsync::Future<void> &future) {
66 QObject::connect(job, &KJob::result, [&future](KJob *job) {
67 SinkTrace() << "Job done: " << job->metaObject()->className();
68 if (job->error()) {
69 SinkWarning() << "Job failed: " << job->errorString() << job->metaObject()->className() << job->error() << static_cast<KDAV2::DavJobBase*>(job)->latestResponseCode();
70 future.setError(translateDavError(job), job->errorString());
71 } else {
72 future.setFinished();
73 }
74 });
75 SinkTrace() << "Starting job: " << job->metaObject()->className();
76 job->start();
77 });
78}
79
80class ContactSynchronizer : public Sink::Synchronizer {
81public:
82 ContactSynchronizer(const Sink::ResourceContext &resourceContext)
83 : Sink::Synchronizer(resourceContext)
84 {
85
86 }
87
88 QByteArray createAddressbook(const QString &addressbookName, const QString &addressbookPath, const QString &parentAddressbookRid)
89 {
90 SinkTrace() << "Creating addressbook: " << addressbookName << parentAddressbookRid;
91 const auto remoteId = addressbookPath.toUtf8();
92 const auto bufferType = ENTITY_TYPE_ADDRESSBOOK;
93 Sink::ApplicationDomain::Addressbook addressbook;
94 addressbook.setName(addressbookName);
95 QHash<QByteArray, Query::Comparator> mergeCriteria;
96
97 if (!parentAddressbookRid.isEmpty()) {
98 addressbook.setParent(syncStore().resolveRemoteId(ENTITY_TYPE_ADDRESSBOOK, parentAddressbookRid.toUtf8()));
99 }
100 createOrModify(bufferType, remoteId, addressbook, mergeCriteria);
101 return remoteId;
102 }
103
104 void synchronizeAddressbooks(const KDAV2::DavCollection::List &addressbookList)
105 {
106 const QByteArray bufferType = ENTITY_TYPE_ADDRESSBOOK;
107 SinkTrace() << "Found addressbooks " << addressbookList.size();
108
109 QVector<QByteArray> ridList;
110 for(const auto &f : addressbookList) {
111 const auto &rid = getRid(f);
112 SinkLog() << "Found addressbook:" << rid << f.displayName();
113 ridList.append(rid);
114 createAddressbook(f.displayName(), rid, "");
115 }
116
117 scanForRemovals(bufferType,
118 [&ridList](const QByteArray &remoteId) -> bool {
119 return ridList.contains(remoteId);
120 }
121 );
122 }
123
124 QList<Synchronizer::SyncRequest> getSyncRequests(const Sink::QueryBase &query) Q_DECL_OVERRIDE
125 {
126 QList<Synchronizer::SyncRequest> list;
127 if (!query.type().isEmpty()) {
128 //We want to synchronize something specific
129 list << Synchronizer::SyncRequest{query};
130 } else {
131 //We want to synchronize everything
132 list << Synchronizer::SyncRequest{Sink::QueryBase(ApplicationDomain::getTypeName<ApplicationDomain::Addressbook>())};
133 list << Synchronizer::SyncRequest{Sink::QueryBase(ApplicationDomain::getTypeName<ApplicationDomain::Contact>())};
134 }
135 return list;
136 }
137
138 static QByteArray getRid(const KDAV2::DavItem &item)
139 {
140 return item.url().toDisplayString().toUtf8();
141 }
142
143 static QByteArray getRid(const KDAV2::DavCollection &item)
144 {
145 return item.url().toDisplayString().toUtf8();
146 }
147
148 KAsync::Job<void> synchronizeWithSource(const Sink::QueryBase &query) Q_DECL_OVERRIDE
149 {
150 if (query.type() == ApplicationDomain::getTypeName<ApplicationDomain::Addressbook>()) {
151 SinkLogCtx(mLogCtx) << "Synchronizing addressbooks:" << resourceUrl().url();
152 auto collectionsFetchJob = new KDAV2::DavCollectionsFetchJob(resourceUrl());
153 auto job = runJob(collectionsFetchJob).then([this, collectionsFetchJob] (const KAsync::Error &error) {
154 if (error) {
155 SinkWarningCtx(mLogCtx) << "Failed to synchronize addressbooks." << collectionsFetchJob->errorString();
156 } else {
157 synchronizeAddressbooks(collectionsFetchJob->collections());
158 }
159 });
160 return job;
161 } else if (query.type() == ApplicationDomain::getTypeName<ApplicationDomain::Contact>()) {
162 SinkLogCtx(mLogCtx) << "Synchronizing contacts.";
163 auto ridList = QSharedPointer<QByteArrayList>::create();
164 auto total = QSharedPointer<int>::create(0);
165 auto progress = QSharedPointer<int>::create(0);
166 auto collectionsFetchJob = new KDAV2::DavCollectionsFetchJob(resourceUrl());
167 auto job = runJob(collectionsFetchJob).then([this, collectionsFetchJob] {
168 synchronizeAddressbooks(collectionsFetchJob ->collections());
169 return collectionsFetchJob->collections();
170 })
171 .serialEach([=](const KDAV2::DavCollection &collection) {
172 auto collId = getRid(collection);
173 const auto addressbookLocalId = syncStore().resolveRemoteId(ENTITY_TYPE_ADDRESSBOOK, collId);
174 auto ctag = collection.CTag().toLatin1();
175 if (ctag != syncStore().readValue(collId + "_ctagXX")) {
176 SinkTraceCtx(mLogCtx) << "Syncing " << collId;
177 auto cache = std::shared_ptr<KDAV2::EtagCache>(new KDAV2::EtagCache());
178 auto davItemsListJob = new KDAV2::DavItemsListJob(collection.url(), cache);
179 QHash<QByteArray, Query::Comparator> mergeCriteria;
180 auto colljob = runJob(davItemsListJob).then([=] {
181 const auto items = davItemsListJob->items();
182 *total = items.size();
183 return KAsync::value(items);
184 })
185 .serialEach([=] (const KDAV2::DavItem &item) {
186 QByteArray rid = getRid(item);
187 if (item.etag().toLatin1() != syncStore().readValue(rid + "_etag")){
188 SinkTrace() << "Updating " << rid;
189 auto davItemFetchJob = new KDAV2::DavItemFetchJob(item);
190 auto itemjob = runJob(davItemFetchJob)
191 .then([=] {
192 const auto item = davItemFetchJob->item();
193 const auto rid = getRid(item);
194 Sink::ApplicationDomain::Contact contact;
195 contact.setVcard(item.data());
196 contact.setAddressbook(addressbookLocalId);
197 createOrModify(ENTITY_TYPE_CONTACT, rid, contact, mergeCriteria);
198 return item;
199 })
200 .then([=] (const KDAV2::DavItem &item) {
201 const auto rid = getRid(item);
202 syncStore().writeValue(rid + "_etag", item.etag().toLatin1());
203 ridList->append(rid);
204 *progress += 1;
205 reportProgress(*progress, *total, QByteArrayList{} << addressbookLocalId);
206 //commit every 5 contacts (so contacts start appearing in the UI)
207 if ((*progress % 5) == 0) {
208 commit();
209 }
210 return rid;
211 });
212 return itemjob;
213 } else {
214 ridList->append(rid);
215 return KAsync::value(rid);
216 }
217 })
218 .then([=] () {
219 syncStore().writeValue(collId + "_ctag", ctag);
220 });
221 return colljob;
222 } else {
223 SinkTraceCtx(mLogCtx) << "Collection unchanged: " << ctag;
224 // for(const auto &item : addressbook) {
225 // ridList->append(rid);
226 // }
227 return KAsync::null<void>();
228 }
229 })
230 .then<void>([this, ridList] () {
231 scanForRemovals(ENTITY_TYPE_CONTACT,
232 [&ridList](const QByteArray &remoteId) -> bool {
233 return ridList->contains(remoteId);
234 });
235 });
236 return job;
237 } else {
238 return KAsync::null<void>();
239 }
240 }
241
242KAsync::Job<QByteArray> replay(const ApplicationDomain::Contact &contact, Sink::Operation operation, const QByteArray &oldRemoteId, const QList<QByteArray> &changedProperties) Q_DECL_OVERRIDE
243 {
244 return KAsync::null<QByteArray>();
245 }
246
247 KAsync::Job<QByteArray> replay(const ApplicationDomain::Addressbook &addressbook, Sink::Operation operation, const QByteArray &oldRemoteId, const QList<QByteArray> &changedProperties) Q_DECL_OVERRIDE
248 {
249 return KAsync::null<QByteArray>();
250 }
251
252 KDAV2::DavUrl resourceUrl() const
253 {
254 if (secret().isEmpty()) {
255 return {};
256 }
257 auto resourceUrl = mServer;
258 resourceUrl.setUserName(mUsername);
259 resourceUrl.setPassword(secret());
260 return KDAV2::DavUrl{resourceUrl, KDAV2::CardDav};
261 }
262
263public:
264 QUrl mServer;
265 QString mUsername;
266};
267
268
269DavResource::DavResource(const Sink::ResourceContext &resourceContext)
270 : Sink::GenericResource(resourceContext)
271{
272 auto config = ResourceConfig::getConfiguration(resourceContext.instanceId());
273 auto server = QUrl::fromUserInput(config.value("server").toString());
274 auto username = config.value("username").toString();
275
276 auto synchronizer = QSharedPointer<ContactSynchronizer>::create(resourceContext);
277 synchronizer->mServer = server;
278 synchronizer->mUsername = username;
279 setupSynchronizer(synchronizer);
280
281 setupPreprocessors(ENTITY_TYPE_CONTACT, QVector<Sink::Preprocessor*>() << new ContactPropertyExtractor);
282}
283
284
285DavResourceFactory::DavResourceFactory(QObject *parent)
286 : Sink::ResourceFactory(parent,
287 {Sink::ApplicationDomain::ResourceCapabilities::Contact::contact,
288 Sink::ApplicationDomain::ResourceCapabilities::Contact::addressbook,
289 Sink::ApplicationDomain::ResourceCapabilities::Contact::storage
290 }
291 )
292{
293}
294
295Sink::Resource *DavResourceFactory::createResource(const ResourceContext &context)
296{
297 return new DavResource(context);
298}
299
300void DavResourceFactory::registerFacades(const QByteArray &name, Sink::FacadeFactory &factory)
301{
302 factory.registerFacade<ApplicationDomain::Contact, DefaultFacade<ApplicationDomain::Contact>>(name);
303 factory.registerFacade<ApplicationDomain::Addressbook, DefaultFacade<ApplicationDomain::Addressbook>>(name);
304}
305
306void DavResourceFactory::registerAdaptorFactories(const QByteArray &name, Sink::AdaptorFactoryRegistry &registry)
307{
308 registry.registerFactory<ApplicationDomain::Contact, DefaultAdaptorFactory<ApplicationDomain::Contact>>(name);
309 registry.registerFactory<ApplicationDomain::Addressbook, DefaultAdaptorFactory<ApplicationDomain::Addressbook>>(name);
310}
311
312void DavResourceFactory::removeDataFromDisk(const QByteArray &instanceIdentifier)
313{
314 DavResource::removeFromDisk(instanceIdentifier);
315}
diff --git a/examples/dummyresource/resourcefactory.cpp b/examples/dummyresource/resourcefactory.cpp
index 275371d..cfce6e4 100644
--- a/examples/dummyresource/resourcefactory.cpp
+++ b/examples/dummyresource/resourcefactory.cpp
@@ -54,12 +54,10 @@ class DummySynchronizer : public Sink::Synchronizer {
54 54
55 Sink::ApplicationDomain::Event::Ptr createEvent(const QByteArray &ridBuffer, const QMap<QString, QVariant> &data) 55 Sink::ApplicationDomain::Event::Ptr createEvent(const QByteArray &ridBuffer, const QMap<QString, QVariant> &data)
56 { 56 {
57 static uint8_t rawData[100];
58 auto event = Sink::ApplicationDomain::Event::Ptr::create(); 57 auto event = Sink::ApplicationDomain::Event::Ptr::create();
59 event->setSummary(data.value("summary").toString()); 58 event->setSummary(data.value("summary").toString());
60 event->setProperty("remoteId", ridBuffer); 59 event->setProperty("remoteId", ridBuffer);
61 event->setDescription(data.value("description").toString()); 60 event->setDescription(data.value("description").toString());
62 event->setAttachment(QByteArray::fromRawData(reinterpret_cast<const char*>(rawData), 100));
63 return event; 61 return event;
64 } 62 }
65 63
diff --git a/examples/webdavcommon/CMakeLists.txt b/examples/webdavcommon/CMakeLists.txt
new file mode 100644
index 0000000..318756e
--- /dev/null
+++ b/examples/webdavcommon/CMakeLists.txt
@@ -0,0 +1,8 @@
1project(sink_webdav_common)
2
3set(CMAKE_CXX_STANDARD 14)
4
5find_package(KPimKDAV2 REQUIRED)
6
7add_library(${PROJECT_NAME} STATIC webdav.cpp)
8target_link_libraries(${PROJECT_NAME} Qt5::Core Qt5::Network KPim::KDAV2)
diff --git a/examples/webdavcommon/webdav.cpp b/examples/webdavcommon/webdav.cpp
new file mode 100644
index 0000000..35f7fc2
--- /dev/null
+++ b/examples/webdavcommon/webdav.cpp
@@ -0,0 +1,277 @@
1/*
2 * Copyright (C) 2018 Christian Mollekopf <chrigi_1@fastmail.fm>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20#include "webdav.h"
21
22#include "applicationdomaintype.h"
23#include "resourceconfig.h"
24
25#include <KDAV2/DavCollectionsFetchJob>
26#include <KDAV2/DavItemFetchJob>
27#include <KDAV2/DavItemsListJob>
28#include <KDAV2/EtagCache>
29
30#include <QNetworkReply>
31
32static int translateDavError(KJob *job)
33{
34 using Sink::ApplicationDomain::ErrorCode;
35
36 const int responseCode = dynamic_cast<KDAV2::DavJobBase *>(job)->latestResponseCode();
37
38 switch (responseCode) {
39 case QNetworkReply::HostNotFoundError:
40 return ErrorCode::NoServerError;
41 // Since we don't login we will just not have the necessary permissions ot view the object
42 case QNetworkReply::OperationCanceledError:
43 return ErrorCode::LoginError;
44 }
45 return ErrorCode::UnknownError;
46}
47
48static KAsync::Job<void> runJob(QSharedPointer<KJob> job)
49{
50 return KAsync::start<void>([job(std::move(job))] {
51 if (job->exec()) {
52 SinkTrace() << "Job exec success";
53 } else {
54 SinkTrace() << "Job exec failure";
55 }
56 });
57
58 // For some reason, this code doesn't work
59
60 /*
61 return KAsync::start<void>([job](KAsync::Future<void> &future) {
62 QObject::connect(job, &KJob::result, [&future](KJob *job) {
63 SinkTrace() << "Job done: " << job->metaObject()->className();
64 if (job->error()) {
65 SinkWarning()
66 << "Job failed: " << job->errorString() << job->metaObject()->className()
67 << job->error() << static_cast<KDAV2::DavJobBase *>(job)->latestResponseCode();
68 future.setError(translateDavError(job), job->errorString());
69 } else {
70 future.setFinished();
71 }
72 });
73 SinkTrace() << "Starting job: " << job->metaObject()->className();
74 job->start();
75 });
76 */
77}
78
79WebDavSynchronizer::WebDavSynchronizer(const Sink::ResourceContext &context,
80 KDAV2::Protocol protocol, QByteArray collectionName, QByteArray itemName)
81 : Sink::Synchronizer(context),
82 protocol(protocol),
83 collectionName(std::move(collectionName)),
84 itemName(std::move(itemName))
85{
86 auto config = ResourceConfig::getConfiguration(context.instanceId());
87
88 server = QUrl::fromUserInput(config.value("server").toString());
89 username = config.value("username").toString();
90}
91
92QList<Sink::Synchronizer::SyncRequest> WebDavSynchronizer::getSyncRequests(const Sink::QueryBase &query)
93{
94 QList<Synchronizer::SyncRequest> list;
95 if (!query.type().isEmpty()) {
96 // We want to synchronize something specific
97 list << Synchronizer::SyncRequest{ query };
98 } else {
99 // We want to synchronize everything
100
101 // Item synchronization does the collections anyway
102 // list << Synchronizer::SyncRequest{ Sink::QueryBase(collectionName) };
103 list << Synchronizer::SyncRequest{ Sink::QueryBase(itemName) };
104 }
105 return list;
106}
107
108KAsync::Job<void> WebDavSynchronizer::synchronizeWithSource(const Sink::QueryBase &query)
109{
110 if (query.type() != collectionName && query.type() != itemName) {
111 return KAsync::null<void>();
112 }
113
114 SinkLog() << "Synchronizing" << query.type() << "through WebDAV at:" << serverUrl().url();
115
116 auto collectionsFetchJob = QSharedPointer<KDAV2::DavCollectionsFetchJob>::create(serverUrl());
117
118 auto job = runJob(collectionsFetchJob).then([this, collectionsFetchJob](const KAsync::Error &error) {
119 if (error) {
120 SinkWarning() << "Failed to synchronize collections:" << collectionsFetchJob->errorString();
121 } else {
122 updateLocalCollections(collectionsFetchJob->collections());
123 }
124
125 return collectionsFetchJob->collections();
126 });
127
128 if (query.type() == collectionName) {
129 // Do nothing more
130 return job;
131 } else if (query.type() == itemName) {
132 auto progress = QSharedPointer<int>::create(0);
133 auto total = QSharedPointer<int>::create(0);
134
135 // Will contain the resource Id of all collections to be able to scan
136 // for collections to be removed.
137 auto collectionResourceIDs = QSharedPointer<QSet<QByteArray>>::create();
138
139 // Same but for items.
140 // Quirk: may contain a collection Id (see below)
141 auto itemsResourceIDs = QSharedPointer<QSet<QByteArray>>::create();
142
143 return job
144 .serialEach([this, progress(std::move(progress)), total(std::move(total)), collectionResourceIDs,
145 itemsResourceIDs](const KDAV2::DavCollection &collection) {
146 auto collectionResourceID = resourceID(collection);
147
148 collectionResourceIDs->insert(collectionResourceID);
149
150 if (unchanged(collection)) {
151 SinkTrace() << "Collection unchanged:" << collectionResourceID;
152
153 // It seems that doing this prevent the items in the
154 // collection to be removed when doing scanForRemovals
155 // below (since the collection is unchanged, we do not go
156 // through all of its items).
157 // Behaviour copied from the previous code.
158 itemsResourceIDs->insert(collectionResourceID);
159
160 return KAsync::null<void>();
161 }
162
163 SinkTrace() << "Syncing collection:" << collectionResourceID;
164 return synchronizeCollection(collection, progress, total, itemsResourceIDs);
165 })
166 .then([this, collectionResourceIDs(std::move(collectionResourceIDs)),
167 itemsResourceIDs(std::move(itemsResourceIDs))]() {
168 scanForRemovals(collectionName, [&collectionResourceIDs](const QByteArray &remoteId) {
169 return collectionResourceIDs->contains(remoteId);
170 });
171 scanForRemovals(itemName, [&itemsResourceIDs](const QByteArray &remoteId) {
172 return itemsResourceIDs->contains(remoteId);
173 });
174 });
175 } else {
176 SinkWarning() << "Unknown query type";
177 return KAsync::null<void>();
178 }
179}
180
181KAsync::Job<void> WebDavSynchronizer::synchronizeCollection(const KDAV2::DavCollection &collection,
182 QSharedPointer<int> progress, QSharedPointer<int> total,
183 QSharedPointer<QSet<QByteArray>> itemsResourceIDs)
184{
185 auto collectionRid = resourceID(collection);
186 auto ctag = collection.CTag().toLatin1();
187
188 auto localRid = collectionLocalResourceID(collection);
189
190 // The ETag cache is useless here, since `sinkStore()` IS the cache.
191 auto cache = std::make_shared<KDAV2::EtagCache>();
192 auto davItemsListJob = QSharedPointer<KDAV2::DavItemsListJob>::create(collection.url(), std::move(cache));
193
194 return runJob(davItemsListJob)
195 .then([this, davItemsListJob, total] {
196 auto items = davItemsListJob->items();
197 *total += items.size();
198 return KAsync::value(items);
199 })
200 .serialEach([this, collectionRid, localRid, progress(std::move(progress)), total(std::move(total)),
201 itemsResourceIDs(std::move(itemsResourceIDs))](const KDAV2::DavItem &item) {
202 auto itemRid = resourceID(item);
203
204 itemsResourceIDs->insert(itemRid);
205
206 if (unchanged(item)) {
207 SinkTrace() << "Item unchanged:" << itemRid;
208 return KAsync::null<void>();
209 }
210
211 SinkTrace() << "Syncing item:" << itemRid;
212 return synchronizeItem(item, localRid, progress, total);
213 })
214 .then([this, collectionRid, ctag] {
215 // Update the local CTag to be able to tell if the collection is unchanged
216 syncStore().writeValue(collectionRid + "_ctag", ctag);
217 });
218}
219
220KAsync::Job<void> WebDavSynchronizer::synchronizeItem(const KDAV2::DavItem &item,
221 const QByteArray &collectionLocalRid, QSharedPointer<int> progress, QSharedPointer<int> total)
222{
223 auto etag = item.etag().toLatin1();
224
225 auto itemFetchJob = QSharedPointer<KDAV2::DavItemFetchJob>::create(item);
226 return runJob(itemFetchJob)
227 .then([this, itemFetchJob(std::move(itemFetchJob)), collectionLocalRid] {
228 auto item = itemFetchJob->item();
229 updateLocalItem(item, collectionLocalRid);
230 return item;
231 })
232 .then([this, etag, progress(std::move(progress)), total(std::move(total))](const KDAV2::DavItem &item) {
233 // Update the local ETag to be able to tell if the item is unchanged
234 syncStore().writeValue(resourceID(item) + "_etag", etag);
235
236 *progress += 1;
237 reportProgress(*progress, *total);
238 if ((*progress % 5) == 0) {
239 commit();
240 }
241 });
242}
243
244QByteArray WebDavSynchronizer::resourceID(const KDAV2::DavCollection &collection)
245{
246 return collection.url().toDisplayString().toUtf8();
247}
248
249QByteArray WebDavSynchronizer::resourceID(const KDAV2::DavItem &item)
250{
251 return item.url().toDisplayString().toUtf8();
252}
253
254bool WebDavSynchronizer::unchanged(const KDAV2::DavCollection &collection)
255{
256 auto ctag = collection.CTag().toLatin1();
257 return ctag == syncStore().readValue(resourceID(collection) + "_ctag");
258}
259
260bool WebDavSynchronizer::unchanged(const KDAV2::DavItem &item)
261{
262 auto etag = item.etag().toLatin1();
263 return etag == syncStore().readValue(resourceID(item) + "_etag");
264}
265
266KDAV2::DavUrl WebDavSynchronizer::serverUrl() const
267{
268 if (secret().isEmpty()) {
269 return {};
270 }
271
272 auto result = server;
273 result.setUserName(username);
274 result.setPassword(secret());
275
276 return KDAV2::DavUrl{ result, protocol };
277}
diff --git a/examples/webdavcommon/webdav.h b/examples/webdavcommon/webdav.h
new file mode 100644
index 0000000..3a4977c
--- /dev/null
+++ b/examples/webdavcommon/webdav.h
@@ -0,0 +1,78 @@
1/*
2 * Copyright (C) 2018 Christian Mollekopf <chrigi_1@fastmail.fm>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20#pragma once
21
22#include "synchronizer.h"
23
24#include <KDAV2/DavCollection>
25#include <KDAV2/DavItem>
26#include <KDAV2/DavUrl>
27
28class WebDavSynchronizer : public Sink::Synchronizer
29{
30public:
31 WebDavSynchronizer(const Sink::ResourceContext &, KDAV2::Protocol, QByteArray collectionName,
32 QByteArray itemName);
33
34 QList<Synchronizer::SyncRequest> getSyncRequests(const Sink::QueryBase &query) Q_DECL_OVERRIDE;
35 KAsync::Job<void> synchronizeWithSource(const Sink::QueryBase &query) Q_DECL_OVERRIDE;
36
37protected:
38 /**
39 * Called with the list of discovered collections. It's purpose should be
40 * adding the said collections to the store.
41 */
42 virtual void updateLocalCollections(KDAV2::DavCollection::List collections) = 0;
43
44 /**
45 * Called when discovering a new item, or when an item has been modified.
46 * It's purpose should be adding the said item to the store.
47 *
48 * `collectionLocalRid` is the local resource id of the collection the item
49 * is in.
50 */
51 virtual void updateLocalItem(KDAV2::DavItem item, const QByteArray &collectionLocalRid) = 0;
52
53 /**
54 * Get the local resource id from a collection.
55 */
56 virtual QByteArray collectionLocalResourceID(const KDAV2::DavCollection &collection) = 0;
57
58 KAsync::Job<void> synchronizeCollection(const KDAV2::DavCollection &,
59 QSharedPointer<int> progress, QSharedPointer<int> total, QSharedPointer<QSet<QByteArray>> itemsResourceIDs);
60 KAsync::Job<void> synchronizeItem(const KDAV2::DavItem &, const QByteArray &collectionLocalRid,
61 QSharedPointer<int> progress, QSharedPointer<int> total);
62
63 static QByteArray resourceID(const KDAV2::DavCollection &);
64 static QByteArray resourceID(const KDAV2::DavItem &);
65
66 bool unchanged(const KDAV2::DavCollection &);
67 bool unchanged(const KDAV2::DavItem &);
68
69 KDAV2::DavUrl serverUrl() const;
70
71private:
72 KDAV2::Protocol protocol;
73 const QByteArray collectionName;
74 const QByteArray itemName;
75
76 QUrl server;
77 QString username;
78};
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 883a38b..fe7866c 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -58,7 +58,6 @@ auto_tests (
58 entitystoretest 58 entitystoretest
59 upgradetest 59 upgradetest
60) 60)
61generate_flatbuffers(sink_test calendar)
62target_link_libraries(dummyresourcetest sink_resource_dummy) 61target_link_libraries(dummyresourcetest sink_resource_dummy)
63target_link_libraries(dummyresourcebenchmark sink_resource_dummy) 62target_link_libraries(dummyresourcebenchmark sink_resource_dummy)
64target_link_libraries(dummyresourcewritebenchmark sink_resource_dummy) 63target_link_libraries(dummyresourcewritebenchmark sink_resource_dummy)
diff --git a/tests/calendar.fbs b/tests/calendar.fbs
deleted file mode 100644
index ef4f112..0000000
--- a/tests/calendar.fbs
+++ /dev/null
@@ -1,12 +0,0 @@
1// example IDL file
2
3namespace Calendar;
4
5table Event {
6 summary:string;
7 description:string;
8 attachment:[ubyte];
9}
10
11root_type Event;
12file_identifier "AKFB";
diff --git a/tests/domainadaptortest.cpp b/tests/domainadaptortest.cpp
index 2aed0a9..60d5069 100644
--- a/tests/domainadaptortest.cpp
+++ b/tests/domainadaptortest.cpp
@@ -76,12 +76,10 @@ private slots:
76 auto summary = m_fbb.CreateString("summary1"); 76 auto summary = m_fbb.CreateString("summary1");
77 auto description = m_fbb.CreateString("description"); 77 auto description = m_fbb.CreateString("description");
78 static uint8_t rawData[100]; 78 static uint8_t rawData[100];
79 auto attachment = m_fbb.CreateVector(rawData, 100);
80 79
81 auto builder = Sink::ApplicationDomain::Buffer::EventBuilder(m_fbb); 80 auto builder = Sink::ApplicationDomain::Buffer::EventBuilder(m_fbb);
82 builder.add_summary(summary); 81 builder.add_summary(summary);
83 builder.add_description(description); 82 builder.add_description(description);
84 builder.add_attachment(attachment);
85 auto buffer = builder.Finish(); 83 auto buffer = builder.Finish();
86 Sink::ApplicationDomain::Buffer::FinishEventBuffer(m_fbb, buffer); 84 Sink::ApplicationDomain::Buffer::FinishEventBuffer(m_fbb, buffer);
87 85
diff --git a/tests/storagebenchmark.cpp b/tests/storagebenchmark.cpp
index ee336d2..eef360e 100644
--- a/tests/storagebenchmark.cpp
+++ b/tests/storagebenchmark.cpp
@@ -1,6 +1,6 @@
1#include <QtTest> 1#include <QtTest>
2 2
3#include "calendar_generated.h" 3#include "event_generated.h"
4 4
5#include "hawd/dataset.h" 5#include "hawd/dataset.h"
6#include "hawd/formatter.h" 6#include "hawd/formatter.h"
@@ -13,7 +13,7 @@
13#include <QString> 13#include <QString>
14#include <QTime> 14#include <QTime>
15 15
16using namespace Calendar; 16using namespace Sink::ApplicationDomain::Buffer;
17using namespace flatbuffers; 17using namespace flatbuffers;
18 18
19static QByteArray createEvent() 19static QByteArray createEvent()
@@ -25,13 +25,10 @@ static QByteArray createEvent()
25 { 25 {
26 uint8_t *rawDataPtr = Q_NULLPTR; 26 uint8_t *rawDataPtr = Q_NULLPTR;
27 auto summary = fbb.CreateString("summary"); 27 auto summary = fbb.CreateString("summary");
28 auto data = fbb.CreateUninitializedVector<uint8_t>(attachmentSize, &rawDataPtr); 28 EventBuilder eventBuilder(fbb);
29 // auto data = fbb.CreateVector(rawData, attachmentSize);
30 Calendar::EventBuilder eventBuilder(fbb);
31 eventBuilder.add_summary(summary); 29 eventBuilder.add_summary(summary);
32 eventBuilder.add_attachment(data);
33 auto eventLocation = eventBuilder.Finish(); 30 auto eventLocation = eventBuilder.Finish();
34 Calendar::FinishEventBuffer(fbb, eventLocation); 31 FinishEventBuffer(fbb, eventLocation);
35 memcpy((void *)rawDataPtr, rawData, attachmentSize); 32 memcpy((void *)rawDataPtr, rawData, attachmentSize);
36 } 33 }
37 34