diff options
-rw-r--r-- | common/clientapi.h | 26 | ||||
-rw-r--r-- | common/pipeline.cpp | 10 | ||||
-rw-r--r-- | common/test/clientapitest.cpp | 14 | ||||
-rw-r--r-- | dummyresource/facade.cpp | 23 | ||||
-rw-r--r-- | dummyresource/facade.h | 4 | ||||
-rw-r--r-- | dummyresource/resourcefactory.cpp | 4 | ||||
-rw-r--r-- | tests/dummyresourcetest.cpp | 53 |
7 files changed, 84 insertions, 50 deletions
diff --git a/common/clientapi.h b/common/clientapi.h index 2f1c127..dd11a0d 100644 --- a/common/clientapi.h +++ b/common/clientapi.h | |||
@@ -165,7 +165,7 @@ namespace Akonadi2 { | |||
165 | /** | 165 | /** |
166 | * Standardized Domain Types | 166 | * Standardized Domain Types |
167 | * | 167 | * |
168 | * The don't adhere to any standard and can be freely extended | 168 | * They don't adhere to any standard and can be freely extended |
169 | * Their sole purpose is providing a standardized interface to access data. | 169 | * Their sole purpose is providing a standardized interface to access data. |
170 | * | 170 | * |
171 | * This is necessary to decouple resource-backends from application domain containers (otherwise each resource would have to provide a faceade for each application domain container). | 171 | * This is necessary to decouple resource-backends from application domain containers (otherwise each resource would have to provide a faceade for each application domain container). |
@@ -297,6 +297,7 @@ using namespace async; | |||
297 | class Query | 297 | class Query |
298 | { | 298 | { |
299 | public: | 299 | public: |
300 | Query() : syncOnDemand(true) {} | ||
300 | //Could also be a propertyFilter | 301 | //Could also be a propertyFilter |
301 | QStringList resources; | 302 | QStringList resources; |
302 | //Could also be a propertyFilter | 303 | //Could also be a propertyFilter |
@@ -305,6 +306,7 @@ public: | |||
305 | QHash<QString, QVariant> propertyFilter; | 306 | QHash<QString, QVariant> propertyFilter; |
306 | //Properties to retrieve | 307 | //Properties to retrieve |
307 | QSet<QString> requestedProperties; | 308 | QSet<QString> requestedProperties; |
309 | bool syncOnDemand; | ||
308 | }; | 310 | }; |
309 | 311 | ||
310 | 312 | ||
@@ -324,7 +326,7 @@ public: | |||
324 | virtual Async::Job<void> create(const DomainType &domainObject) = 0; | 326 | virtual Async::Job<void> create(const DomainType &domainObject) = 0; |
325 | virtual Async::Job<void> modify(const DomainType &domainObject) = 0; | 327 | virtual Async::Job<void> modify(const DomainType &domainObject) = 0; |
326 | virtual Async::Job<void> remove(const DomainType &domainObject) = 0; | 328 | virtual Async::Job<void> remove(const DomainType &domainObject) = 0; |
327 | virtual void load(const Query &query, const std::function<void(const typename DomainType::Ptr &)> &resultCallback, const std::function<void()> &completeCallback) = 0; | 329 | virtual Async::Job<void> load(const Query &query, const std::function<void(const typename DomainType::Ptr &)> &resultCallback) = 0; |
328 | }; | 330 | }; |
329 | 331 | ||
330 | 332 | ||
@@ -418,21 +420,25 @@ public: | |||
418 | // query tells us in which resources we're interested | 420 | // query tells us in which resources we're interested |
419 | // TODO: queries to individual resources could be parallelized | 421 | // TODO: queries to individual resources could be parallelized |
420 | auto eventloop = QSharedPointer<QEventLoop>::create(); | 422 | auto eventloop = QSharedPointer<QEventLoop>::create(); |
421 | int completeCounter = 0; | 423 | Async::Job<void> job = Async::null<void>(); |
422 | for(const QString &resource : query.resources) { | 424 | for(const QString &resource : query.resources) { |
423 | auto facade = FacadeFactory::instance().getFacade<DomainType>(resource); | 425 | auto facade = FacadeFactory::instance().getFacade<DomainType>(resource); |
424 | //We have to bind an instance to the function callback. Since we use a shared pointer this keeps the result provider instance (and thus also the emitter) alive. | 426 | //We have to bind an instance to the function callback. Since we use a shared pointer this keeps the result provider instance (and thus also the emitter) alive. |
425 | std::function<void(const typename DomainType::Ptr &)> addCallback = std::bind(&ResultProvider<typename DomainType::Ptr>::add, resultSet, std::placeholders::_1); | 427 | std::function<void(const typename DomainType::Ptr &)> addCallback = std::bind(&ResultProvider<typename DomainType::Ptr>::add, resultSet, std::placeholders::_1); |
426 | //We copy the facade pointer to keep it alive | 428 | //We copy the facade pointer to keep it alive |
427 | facade->load(query, addCallback, [&completeCounter, &query, resultSet, facade, eventloop]() { | 429 | job = job.then<void>([facade, query, addCallback](Async::Future<void> &future) { |
428 | //TODO use jobs instead of this counter | 430 | Async::Job<void> j = facade->load(query, addCallback); |
429 | completeCounter++; | 431 | j.then<void>([&future, facade](Async::Future<void> &f) { |
430 | if (completeCounter == query.resources.size()) { | 432 | future.setFinished(); |
431 | resultSet->complete(); | 433 | f.setFinished(); |
432 | eventloop->quit(); | 434 | }).exec(); |
433 | } | ||
434 | }); | 435 | }); |
435 | } | 436 | } |
437 | job.then<void>([eventloop, resultSet](Async::Future<void> &future) { | ||
438 | resultSet->complete(); | ||
439 | eventloop->quit(); | ||
440 | future.setFinished(); | ||
441 | }).exec(); | ||
436 | //The thread contains no eventloop, so execute one here | 442 | //The thread contains no eventloop, so execute one here |
437 | eventloop->exec(QEventLoop::ExcludeUserInputEvents); | 443 | eventloop->exec(QEventLoop::ExcludeUserInputEvents); |
438 | }); | 444 | }); |
diff --git a/common/pipeline.cpp b/common/pipeline.cpp index 339a39c..dda7671 100644 --- a/common/pipeline.cpp +++ b/common/pipeline.cpp | |||
@@ -106,15 +106,21 @@ Async::Job<void> Pipeline::newEntity(void const *command, size_t size) | |||
106 | { | 106 | { |
107 | flatbuffers::Verifier verifyer(reinterpret_cast<const uint8_t *>(command), size); | 107 | flatbuffers::Verifier verifyer(reinterpret_cast<const uint8_t *>(command), size); |
108 | if (!Akonadi2::Commands::VerifyCreateEntityBuffer(verifyer)) { | 108 | if (!Akonadi2::Commands::VerifyCreateEntityBuffer(verifyer)) { |
109 | qWarning() << "invalid buffer"; | 109 | qWarning() << "invalid buffer, not a create entity buffer"; |
110 | return Async::null<void>(); | 110 | return Async::null<void>(); |
111 | } | 111 | } |
112 | } | 112 | } |
113 | |||
114 | auto createEntity = Akonadi2::Commands::GetCreateEntity(command); | 113 | auto createEntity = Akonadi2::Commands::GetCreateEntity(command); |
115 | 114 | ||
116 | //TODO rename createEntitiy->domainType to bufferType | 115 | //TODO rename createEntitiy->domainType to bufferType |
117 | const QString entityType = QString::fromUtf8(reinterpret_cast<char const*>(createEntity->domainType()->Data()), createEntity->domainType()->size()); | 116 | const QString entityType = QString::fromUtf8(reinterpret_cast<char const*>(createEntity->domainType()->Data()), createEntity->domainType()->size()); |
117 | { | ||
118 | flatbuffers::Verifier verifyer(reinterpret_cast<const uint8_t *>(createEntity->delta()->Data()), createEntity->delta()->size()); | ||
119 | if (!Akonadi2::VerifyEntityBuffer(verifyer)) { | ||
120 | qWarning() << "invalid buffer, not an entity buffer"; | ||
121 | return Async::null<void>(); | ||
122 | } | ||
123 | } | ||
118 | auto entity = Akonadi2::GetEntity(createEntity->delta()->Data()); | 124 | auto entity = Akonadi2::GetEntity(createEntity->delta()->Data()); |
119 | 125 | ||
120 | //Add metadata buffer | 126 | //Add metadata buffer |
diff --git a/common/test/clientapitest.cpp b/common/test/clientapitest.cpp index 16616a3..c9e4d6d 100644 --- a/common/test/clientapitest.cpp +++ b/common/test/clientapitest.cpp | |||
@@ -11,13 +11,15 @@ public: | |||
11 | virtual Async::Job<void> create(const Akonadi2::Domain::Event &domainObject){ return Async::null<void>(); }; | 11 | virtual Async::Job<void> create(const Akonadi2::Domain::Event &domainObject){ return Async::null<void>(); }; |
12 | virtual Async::Job<void> modify(const Akonadi2::Domain::Event &domainObject){ return Async::null<void>(); }; | 12 | virtual Async::Job<void> modify(const Akonadi2::Domain::Event &domainObject){ return Async::null<void>(); }; |
13 | virtual Async::Job<void> remove(const Akonadi2::Domain::Event &domainObject){ return Async::null<void>(); }; | 13 | virtual Async::Job<void> remove(const Akonadi2::Domain::Event &domainObject){ return Async::null<void>(); }; |
14 | virtual void load(const Akonadi2::Query &query, const std::function<void(const Akonadi2::Domain::Event::Ptr &)> &resultCallback, const std::function<void()> &completeCallback) | 14 | virtual Async::Job<void> load(const Akonadi2::Query &query, const std::function<void(const Akonadi2::Domain::Event::Ptr &)> &resultCallback) |
15 | { | 15 | { |
16 | qDebug() << "load called"; | 16 | return Async::start<void>([this, resultCallback](Async::Future<void> &future) { |
17 | for(const auto &result : results) { | 17 | qDebug() << "load called"; |
18 | resultCallback(result); | 18 | for(const auto &result : results) { |
19 | } | 19 | resultCallback(result); |
20 | completeCallback(); | 20 | } |
21 | future.setFinished(); | ||
22 | }); | ||
21 | } | 23 | } |
22 | 24 | ||
23 | QList<Akonadi2::Domain::Event::Ptr> results; | 25 | QList<Akonadi2::Domain::Event::Ptr> results; |
diff --git a/dummyresource/facade.cpp b/dummyresource/facade.cpp index 13c174b..b7ba2c2 100644 --- a/dummyresource/facade.cpp +++ b/dummyresource/facade.cpp | |||
@@ -118,22 +118,29 @@ static std::function<bool(const std::string &key, DummyEvent const *buffer)> pre | |||
118 | return preparedQuery; | 118 | return preparedQuery; |
119 | } | 119 | } |
120 | 120 | ||
121 | void DummyResourceFacade::synchronizeResource(const std::function<void()> &continuation) | 121 | Async::Job<void> DummyResourceFacade::synchronizeResource(bool sync) |
122 | { | 122 | { |
123 | //TODO check if a sync is necessary | 123 | //TODO check if a sync is necessary |
124 | //TODO Only sync what was requested | 124 | //TODO Only sync what was requested |
125 | //TODO timeout | 125 | //TODO timeout |
126 | mResourceAccess->synchronizeResource().then<void>([continuation](Async::Future<void> &f){ | 126 | |
127 | continuation(); | 127 | if (sync) { |
128 | f.setFinished(); | 128 | return Async::start<void>([=](Async::Future<void> &future) { |
129 | }).exec(); | 129 | mResourceAccess->open(); |
130 | mResourceAccess->synchronizeResource().then<void>([&future](Async::Future<void> &f) { | ||
131 | future.setFinished(); | ||
132 | f.setFinished(); | ||
133 | }).exec(); | ||
134 | }); | ||
135 | } | ||
136 | return Async::null<void>(); | ||
130 | } | 137 | } |
131 | 138 | ||
132 | void DummyResourceFacade::load(const Akonadi2::Query &query, const std::function<void(const Akonadi2::Domain::Event::Ptr &)> &resultCallback, const std::function<void()> &completeCallback) | 139 | Async::Job<void> DummyResourceFacade::load(const Akonadi2::Query &query, const std::function<void(const Akonadi2::Domain::Event::Ptr &)> &resultCallback) |
133 | { | 140 | { |
134 | qDebug() << "load called"; | 141 | qDebug() << "load called"; |
135 | 142 | ||
136 | synchronizeResource([=]() { | 143 | return synchronizeResource(query.syncOnDemand).then<void>([=](Async::Future<void> &future) { |
137 | qDebug() << "sync complete"; | 144 | qDebug() << "sync complete"; |
138 | //Now that the sync is complete we can execute the query | 145 | //Now that the sync is complete we can execute the query |
139 | const auto preparedQuery = prepareQuery(query); | 146 | const auto preparedQuery = prepareQuery(query); |
@@ -188,7 +195,7 @@ void DummyResourceFacade::load(const Akonadi2::Query &query, const std::function | |||
188 | } | 195 | } |
189 | return true; | 196 | return true; |
190 | }); | 197 | }); |
191 | completeCallback(); | 198 | future.setFinished(); |
192 | }); | 199 | }); |
193 | } | 200 | } |
194 | 201 | ||
diff --git a/dummyresource/facade.h b/dummyresource/facade.h index 9c8827a..da0b1d6 100644 --- a/dummyresource/facade.h +++ b/dummyresource/facade.h | |||
@@ -40,10 +40,10 @@ public: | |||
40 | virtual Async::Job<void> create(const Akonadi2::Domain::Event &domainObject); | 40 | virtual Async::Job<void> create(const Akonadi2::Domain::Event &domainObject); |
41 | virtual Async::Job<void> modify(const Akonadi2::Domain::Event &domainObject); | 41 | virtual Async::Job<void> modify(const Akonadi2::Domain::Event &domainObject); |
42 | virtual Async::Job<void> remove(const Akonadi2::Domain::Event &domainObject); | 42 | virtual Async::Job<void> remove(const Akonadi2::Domain::Event &domainObject); |
43 | virtual void load(const Akonadi2::Query &query, const std::function<void(const Akonadi2::Domain::Event::Ptr &)> &resultCallback, const std::function<void()> &completeCallback); | 43 | virtual Async::Job<void> load(const Akonadi2::Query &query, const std::function<void(const Akonadi2::Domain::Event::Ptr &)> &resultCallback); |
44 | 44 | ||
45 | private: | 45 | private: |
46 | void synchronizeResource(const std::function<void()> &continuation); | 46 | Async::Job<void> synchronizeResource(bool sync); |
47 | QSharedPointer<Akonadi2::ResourceAccess> mResourceAccess; | 47 | QSharedPointer<Akonadi2::ResourceAccess> mResourceAccess; |
48 | QSharedPointer<DomainTypeAdaptorFactory<Akonadi2::Domain::Event, Akonadi2::Domain::Buffer::Event, DummyCalendar::DummyEvent> > mFactory; | 48 | QSharedPointer<DomainTypeAdaptorFactory<Akonadi2::Domain::Event, Akonadi2::Domain::Buffer::Event, DummyCalendar::DummyEvent> > mFactory; |
49 | }; | 49 | }; |
diff --git a/dummyresource/resourcefactory.cpp b/dummyresource/resourcefactory.cpp index f74eb72..dc716ef 100644 --- a/dummyresource/resourcefactory.cpp +++ b/dummyresource/resourcefactory.cpp | |||
@@ -317,7 +317,9 @@ Async::Job<void> DummyResource::synchronizeWithSource(Akonadi2::Pipeline *pipeli | |||
317 | builder.add_attachment(attachment); | 317 | builder.add_attachment(attachment); |
318 | auto buffer = builder.Finish(); | 318 | auto buffer = builder.Finish(); |
319 | DummyCalendar::FinishDummyEventBuffer(m_fbb, buffer); | 319 | DummyCalendar::FinishDummyEventBuffer(m_fbb, buffer); |
320 | enqueueCommand(mSynchronizerQueue, Akonadi2::Commands::CreateEntityCommand, QByteArray::fromRawData(reinterpret_cast<char const *>(m_fbb.GetBufferPointer()), m_fbb.GetSize())); | 320 | flatbuffers::FlatBufferBuilder entityFbb; |
321 | Akonadi2::EntityBuffer::assembleEntityBuffer(entityFbb, 0, 0, m_fbb.GetBufferPointer(), m_fbb.GetSize(), 0, 0); | ||
322 | enqueueCommand(mSynchronizerQueue, Akonadi2::Commands::CreateEntityCommand, QByteArray::fromRawData(reinterpret_cast<char const *>(entityFbb.GetBufferPointer()), entityFbb.GetSize())); | ||
321 | } else { //modification | 323 | } else { //modification |
322 | //TODO diff and create modification if necessary | 324 | //TODO diff and create modification if necessary |
323 | } | 325 | } |
diff --git a/tests/dummyresourcetest.cpp b/tests/dummyresourcetest.cpp index c469796..a80d22f 100644 --- a/tests/dummyresourcetest.cpp +++ b/tests/dummyresourcetest.cpp | |||
@@ -82,27 +82,38 @@ private Q_SLOTS: | |||
82 | QCOMPARE(revisionSpy.count(), 2); | 82 | QCOMPARE(revisionSpy.count(), 2); |
83 | } | 83 | } |
84 | 84 | ||
85 | // void testResourceSync() | 85 | void testWriteToFacade() |
86 | // { | 86 | { |
87 | // Akonadi2::Pipeline pipeline("org.kde.dummy"); | 87 | Akonadi2::Query query; |
88 | // DummyResource resource; | 88 | Akonadi2::Domain::Event event; |
89 | // auto job = resource.synchronizeWithSource(&pipeline); | 89 | event.setProperty("summary", "summaryValue"); |
90 | // auto future = job.exec(); | 90 | Akonadi2::Store::create<Akonadi2::Domain::Event>(event, "org.kde.dummy"); |
91 | // QTRY_VERIFY(future.isFinished()); | 91 | |
92 | // } | 92 | QTest::qWait(1000); |
93 | 93 | //TODO wait for success response | |
94 | // void testSyncAndFacade() | 94 | } |
95 | // { | 95 | |
96 | // Akonadi2::Query query; | 96 | void testResourceSync() |
97 | // query.resources << "org.kde.dummy"; | 97 | { |
98 | 98 | Akonadi2::Pipeline pipeline("org.kde.dummy"); | |
99 | // async::SyncListResult<Akonadi2::Domain::Event::Ptr> result(Akonadi2::Store::load<Akonadi2::Domain::Event>(query)); | 99 | DummyResource resource; |
100 | // result.exec(); | 100 | auto job = resource.synchronizeWithSource(&pipeline); |
101 | // QVERIFY(!result.isEmpty()); | 101 | auto future = job.exec(); |
102 | // auto value = result.first(); | 102 | QTRY_VERIFY(future.isFinished()); |
103 | // QVERIFY(!value->getProperty("summary").toString().isEmpty()); | 103 | } |
104 | // qDebug() << value->getProperty("summary").toString(); | 104 | |
105 | // } | 105 | void testSyncAndFacade() |
106 | { | ||
107 | Akonadi2::Query query; | ||
108 | query.resources << "org.kde.dummy"; | ||
109 | |||
110 | async::SyncListResult<Akonadi2::Domain::Event::Ptr> result(Akonadi2::Store::load<Akonadi2::Domain::Event>(query)); | ||
111 | result.exec(); | ||
112 | QVERIFY(!result.isEmpty()); | ||
113 | auto value = result.first(); | ||
114 | QVERIFY(!value->getProperty("summary").toString().isEmpty()); | ||
115 | qDebug() << value->getProperty("summary").toString(); | ||
116 | } | ||
106 | 117 | ||
107 | }; | 118 | }; |
108 | 119 | ||