diff options
Diffstat (limited to 'dummyresource')
-rw-r--r-- | dummyresource/dummycalendar.fbs | 1 | ||||
-rw-r--r-- | dummyresource/facade.cpp | 63 | ||||
-rw-r--r-- | dummyresource/facade.h | 9 | ||||
-rw-r--r-- | dummyresource/resourcefactory.cpp | 87 |
4 files changed, 131 insertions, 29 deletions
diff --git a/dummyresource/dummycalendar.fbs b/dummyresource/dummycalendar.fbs index 5a217b5..643c9b2 100644 --- a/dummyresource/dummycalendar.fbs +++ b/dummyresource/dummycalendar.fbs | |||
@@ -6,6 +6,7 @@ table DummyEvent { | |||
6 | summary:string; | 6 | summary:string; |
7 | description:string; | 7 | description:string; |
8 | attachment:[ubyte]; | 8 | attachment:[ubyte]; |
9 | remoteId:string; | ||
9 | } | 10 | } |
10 | 11 | ||
11 | root_type DummyEvent; | 12 | root_type DummyEvent; |
diff --git a/dummyresource/facade.cpp b/dummyresource/facade.cpp index 0d47010..c2871bb 100644 --- a/dummyresource/facade.cpp +++ b/dummyresource/facade.cpp | |||
@@ -23,6 +23,7 @@ | |||
23 | #include <functional> | 23 | #include <functional> |
24 | 24 | ||
25 | #include "common/resourceaccess.h" | 25 | #include "common/resourceaccess.h" |
26 | #include "common/commands.h" | ||
26 | #include "dummycalendar_generated.h" | 27 | #include "dummycalendar_generated.h" |
27 | 28 | ||
28 | using namespace DummyCalendar; | 29 | using namespace DummyCalendar; |
@@ -30,7 +31,7 @@ using namespace flatbuffers; | |||
30 | 31 | ||
31 | DummyResourceFacade::DummyResourceFacade() | 32 | DummyResourceFacade::DummyResourceFacade() |
32 | : Akonadi2::StoreFacade<Akonadi2::Domain::Event>(), | 33 | : Akonadi2::StoreFacade<Akonadi2::Domain::Event>(), |
33 | mResourceAccess(/* new ResourceAccess("dummyresource") */) | 34 | mResourceAccess(new Akonadi2::ResourceAccess("org.kde.dummy")) |
34 | { | 35 | { |
35 | // connect(mResourceAccess.data(), &ResourceAccess::ready, this, onReadyChanged); | 36 | // connect(mResourceAccess.data(), &ResourceAccess::ready, this, onReadyChanged); |
36 | } | 37 | } |
@@ -95,11 +96,8 @@ public: | |||
95 | QSharedPointer<Akonadi2::Storage> storage; | 96 | QSharedPointer<Akonadi2::Storage> storage; |
96 | }; | 97 | }; |
97 | 98 | ||
98 | void DummyResourceFacade::load(const Akonadi2::Query &query, const std::function<void(const Akonadi2::Domain::Event::Ptr &)> &resultCallback) | 99 | static std::function<bool(const std::string &key, DummyEvent const *buffer)> prepareQuery(const Akonadi2::Query &query) |
99 | { | 100 | { |
100 | qDebug() << "load called"; | ||
101 | auto storage = QSharedPointer<Akonadi2::Storage>::create(Akonadi2::Store::storageLocation(), "dummyresource"); | ||
102 | |||
103 | //Compose some functions to make query matching fast. | 101 | //Compose some functions to make query matching fast. |
104 | //This way we can process the query once, and convert all values into something that can be compared quickly | 102 | //This way we can process the query once, and convert all values into something that can be compared quickly |
105 | std::function<bool(const std::string &key, DummyEvent const *buffer)> preparedQuery; | 103 | std::function<bool(const std::string &key, DummyEvent const *buffer)> preparedQuery; |
@@ -125,21 +123,48 @@ void DummyResourceFacade::load(const Akonadi2::Query &query, const std::function | |||
125 | return true; | 123 | return true; |
126 | }; | 124 | }; |
127 | } | 125 | } |
126 | return preparedQuery; | ||
127 | } | ||
128 | 128 | ||
129 | //Because we have no indexes yet, we always do a full scan | 129 | void DummyResourceFacade::synchronizeResource(const std::function<void()> &continuation) |
130 | storage->scan("", [=](void *keyValue, int keySize, void *dataValue, int dataSize) -> bool { | 130 | { |
131 | //TODO read second buffer as well | 131 | //TODO check if a sync is necessary |
132 | auto eventBuffer = GetDummyEvent(dataValue); | 132 | //TODO Only sync what was requested |
133 | if (preparedQuery && preparedQuery(std::string(static_cast<char*>(keyValue), keySize), eventBuffer)) { | 133 | //TODO timeout |
134 | //TODO read the revision from the generic portion of the buffer | 134 | mResourceAccess->open(); |
135 | auto event = QSharedPointer<DummyEventAdaptor>::create("dummyresource", QString::fromUtf8(static_cast<char*>(keyValue), keySize), 0); | 135 | mResourceAccess->synchronizeResource(continuation); |
136 | event->buffer = eventBuffer; | 136 | } |
137 | event->storage = storage; | 137 | |
138 | resultCallback(event); | 138 | void DummyResourceFacade::load(const Akonadi2::Query &query, const std::function<void(const Akonadi2::Domain::Event::Ptr &)> &resultCallback, const std::function<void()> &completeCallback) |
139 | } | 139 | { |
140 | return true; | 140 | qDebug() << "load called"; |
141 | |||
142 | synchronizeResource([=]() { | ||
143 | //Now that the sync is complete we can execute the query | ||
144 | const auto preparedQuery = prepareQuery(query); | ||
145 | |||
146 | auto storage = QSharedPointer<Akonadi2::Storage>::create(Akonadi2::Store::storageLocation(), "org.kde.dummy"); | ||
147 | |||
148 | qDebug() << "executing query"; | ||
149 | //We start a transaction explicitly that we'll leave open so the values can be read. | ||
150 | //The transaction will be closed automatically once the storage object is destroyed. | ||
151 | storage->startTransaction(Akonadi2::Storage::ReadOnly); | ||
152 | //Because we have no indexes yet, we always do a full scan | ||
153 | storage->scan("", [=](void *keyValue, int keySize, void *dataValue, int dataSize) -> bool { | ||
154 | //TODO read the three buffers | ||
155 | qDebug() << QString::fromStdString(std::string(static_cast<char*>(keyValue), keySize)); | ||
156 | auto eventBuffer = GetDummyEvent(dataValue); | ||
157 | if (preparedQuery && preparedQuery(std::string(static_cast<char*>(keyValue), keySize), eventBuffer)) { | ||
158 | //TODO set proper revision | ||
159 | qint64 revision = 0; | ||
160 | auto event = QSharedPointer<DummyEventAdaptor>::create("org.kde.dummy", QString::fromUtf8(static_cast<char*>(keyValue), keySize), revision); | ||
161 | event->buffer = eventBuffer; | ||
162 | event->storage = storage; | ||
163 | resultCallback(event); | ||
164 | } | ||
165 | return true; | ||
166 | }); | ||
167 | completeCallback(); | ||
141 | }); | 168 | }); |
142 | } | 169 | } |
143 | 170 | ||
144 | //TODO call in plugin loader | ||
145 | // Akonadi2::FacadeFactory::instance().registerFacade<Akonadi2::Domain::Event, DummyResourceFacade>("dummyresource", [facade](){ return new DummyResourceFacade(facade); }); | ||
diff --git a/dummyresource/facade.h b/dummyresource/facade.h index f179c06..c76e62c 100644 --- a/dummyresource/facade.h +++ b/dummyresource/facade.h | |||
@@ -22,7 +22,9 @@ | |||
22 | #include "common/clientapi.h" | 22 | #include "common/clientapi.h" |
23 | #include "common/storage.h" | 23 | #include "common/storage.h" |
24 | 24 | ||
25 | class ResourceAccess; | 25 | namespace Akonadi2 { |
26 | class ResourceAccess; | ||
27 | } | ||
26 | 28 | ||
27 | class DummyResourceFacade : public Akonadi2::StoreFacade<Akonadi2::Domain::Event> | 29 | class DummyResourceFacade : public Akonadi2::StoreFacade<Akonadi2::Domain::Event> |
28 | { | 30 | { |
@@ -32,8 +34,9 @@ public: | |||
32 | virtual void create(const Akonadi2::Domain::Event &domainObject); | 34 | virtual void create(const Akonadi2::Domain::Event &domainObject); |
33 | virtual void modify(const Akonadi2::Domain::Event &domainObject); | 35 | virtual void modify(const Akonadi2::Domain::Event &domainObject); |
34 | virtual void remove(const Akonadi2::Domain::Event &domainObject); | 36 | virtual void remove(const Akonadi2::Domain::Event &domainObject); |
35 | virtual void load(const Akonadi2::Query &query, const std::function<void(const Akonadi2::Domain::Event::Ptr &)> &resultCallback); | 37 | virtual void load(const Akonadi2::Query &query, const std::function<void(const Akonadi2::Domain::Event::Ptr &)> &resultCallback, const std::function<void()> &completeCallback); |
36 | 38 | ||
37 | private: | 39 | private: |
38 | QSharedPointer<ResourceAccess> mResourceAccess; | 40 | void synchronizeResource(const std::function<void()> &continuation); |
41 | QSharedPointer<Akonadi2::ResourceAccess> mResourceAccess; | ||
39 | }; | 42 | }; |
diff --git a/dummyresource/resourcefactory.cpp b/dummyresource/resourcefactory.cpp index bd85b4f..2c43981 100644 --- a/dummyresource/resourcefactory.cpp +++ b/dummyresource/resourcefactory.cpp | |||
@@ -20,22 +20,95 @@ | |||
20 | #include "resourcefactory.h" | 20 | #include "resourcefactory.h" |
21 | #include "facade.h" | 21 | #include "facade.h" |
22 | #include "dummycalendar_generated.h" | 22 | #include "dummycalendar_generated.h" |
23 | #include <QUuid> | ||
24 | |||
25 | static std::string createEvent() | ||
26 | { | ||
27 | static const size_t attachmentSize = 1024*2; // 2KB | ||
28 | static uint8_t rawData[attachmentSize]; | ||
29 | static flatbuffers::FlatBufferBuilder fbb; | ||
30 | fbb.Clear(); | ||
31 | { | ||
32 | auto summary = fbb.CreateString("summary"); | ||
33 | auto data = fbb.CreateUninitializedVector<uint8_t>(attachmentSize); | ||
34 | DummyCalendar::DummyEventBuilder eventBuilder(fbb); | ||
35 | eventBuilder.add_summary(summary); | ||
36 | eventBuilder.add_attachment(data); | ||
37 | auto eventLocation = eventBuilder.Finish(); | ||
38 | DummyCalendar::FinishDummyEventBuffer(fbb, eventLocation); | ||
39 | memcpy((void*)DummyCalendar::GetDummyEvent(fbb.GetBufferPointer())->attachment()->Data(), rawData, attachmentSize); | ||
40 | } | ||
41 | |||
42 | return std::string(reinterpret_cast<const char *>(fbb.GetBufferPointer()), fbb.GetSize()); | ||
43 | } | ||
44 | |||
45 | QMap<QString, QString> populate() | ||
46 | { | ||
47 | QMap<QString, QString> content; | ||
48 | for (int i = 0; i < 2; i++) { | ||
49 | auto event = createEvent(); | ||
50 | content.insert(QString("key%1").arg(i), QString::fromStdString(event)); | ||
51 | } | ||
52 | return content; | ||
53 | } | ||
54 | |||
55 | static QMap<QString, QString> s_dataSource = populate(); | ||
23 | 56 | ||
24 | DummyResource::DummyResource() | 57 | DummyResource::DummyResource() |
25 | : Akonadi2::Resource() | 58 | : Akonadi2::Resource() |
26 | { | 59 | { |
60 | } | ||
27 | 61 | ||
62 | void findByRemoteId(QSharedPointer<Akonadi2::Storage> storage, const QString &rid, std::function<void(void *keyValue, int keySize, void *dataValue, int dataSize)> callback) | ||
63 | { | ||
64 | //TODO lookup in rid index instead of doing a full scan | ||
65 | const std::string ridString = rid.toStdString(); | ||
66 | storage->scan("", [&](void *keyValue, int keySize, void *dataValue, int dataSize) -> bool { | ||
67 | auto eventBuffer = DummyCalendar::GetDummyEvent(dataValue); | ||
68 | if (std::string(eventBuffer->remoteId()->c_str(), eventBuffer->remoteId()->size()) == ridString) { | ||
69 | callback(keyValue, keySize, dataValue, dataSize); | ||
70 | } | ||
71 | return true; | ||
72 | }); | ||
28 | } | 73 | } |
29 | 74 | ||
30 | void DummyResource::synchronizeWithSource(Akonadi2::Pipeline *pipeline) | 75 | void DummyResource::synchronizeWithSource(Akonadi2::Pipeline *pipeline) |
31 | { | 76 | { |
32 | // TODO actually populate the storage with new items | 77 | //TODO use a read-only transaction during the complete sync to sync against a defined revision |
33 | auto builder = DummyCalendar::DummyEventBuilder(m_fbb); | 78 | |
34 | builder .add_summary(m_fbb.CreateString("summary summary!")); | 79 | qDebug() << "synchronize with source"; |
35 | auto buffer = builder.Finish(); | 80 | |
36 | DummyCalendar::FinishDummyEventBuffer(m_fbb, buffer); | 81 | auto storage = QSharedPointer<Akonadi2::Storage>::create(Akonadi2::Store::storageLocation(), "org.kde.dummy"); |
37 | pipeline->newEntity("fakekey", m_fbb); | 82 | for (auto it = s_dataSource.constBegin(); it != s_dataSource.constEnd(); it++) { |
38 | m_fbb.Clear(); | 83 | bool isNew = true; |
84 | if (storage->exists()) { | ||
85 | findByRemoteId(storage, it.key(), [&](void *keyValue, int keySize, void *dataValue, int dataSize) { | ||
86 | isNew = false; | ||
87 | }); | ||
88 | } | ||
89 | |||
90 | if (isNew) { | ||
91 | //TODO: perhaps it would be more convenient to populate the domain types? | ||
92 | //Resource specific parts are not accessible that way, but then we would only have to implement the property mapping in one place | ||
93 | const QByteArray data = it.value().toUtf8(); | ||
94 | auto eventBuffer = DummyCalendar::GetDummyEvent(data.data()); | ||
95 | |||
96 | //Map the source format to the buffer format (which happens to be an exact copy here) | ||
97 | auto builder = DummyCalendar::DummyEventBuilder(m_fbb); | ||
98 | builder.add_summary(m_fbb.CreateString(eventBuffer->summary()->c_str())); | ||
99 | auto buffer = builder.Finish(); | ||
100 | DummyCalendar::FinishDummyEventBuffer(m_fbb, buffer); | ||
101 | |||
102 | //TODO toRFC4122 would probably be more efficient, but results in non-printable keys. | ||
103 | const auto key = QUuid::createUuid().toString().toUtf8(); | ||
104 | //TODO can we really just start populating the buffer and pass the buffer builder? | ||
105 | qDebug() << "new event"; | ||
106 | pipeline->newEntity(key, m_fbb); | ||
107 | } else { //modification | ||
108 | //TODO diff and create modification if necessary | ||
109 | } | ||
110 | } | ||
111 | //TODO find items to remove | ||
39 | } | 112 | } |
40 | 113 | ||
41 | void DummyResource::processCommand(int commandId, const QByteArray &data, uint size, Akonadi2::Pipeline *pipeline) | 114 | void DummyResource::processCommand(int commandId, const QByteArray &data, uint size, Akonadi2::Pipeline *pipeline) |