Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lazy loading members #263

Merged
merged 29 commits into from
Dec 14, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
3392e66
Refactor toJson/fillJson
KitsuneRal Dec 8, 2018
9628594
EventContent: minor cleanup
KitsuneRal Dec 8, 2018
a005348
SyncJob: accept Filter instead of QString for the filter
KitsuneRal Nov 23, 2018
1ff8a0c
Connection: support members lazy-loading
KitsuneRal Nov 13, 2018
9272d21
Room summaries
KitsuneRal Dec 8, 2018
4a252aa
Omittable<>::merge<>
KitsuneRal Dec 8, 2018
d51684b
MSC 688: MSC-compliant RoomSummary; update Room::calculateDisplayname()
KitsuneRal Dec 8, 2018
1678296
fromJson, fillFromJson: avoid overwriting pods if the JSON value is u…
KitsuneRal Dec 9, 2018
9225eae
Room: track more changes; fix cache smashing upon restart
KitsuneRal Dec 9, 2018
9b3e437
Room: defer memberListChanged(); track room summary changes
KitsuneRal Dec 9, 2018
501c79f
Room::getPreviousContent: use early return
KitsuneRal Dec 10, 2018
c6720cc
Expose Connection::nextBatchToken()
KitsuneRal Dec 11, 2018
0e67d1e
RoomMemberEvent: properly integrate with GetMembersByRoomJob
KitsuneRal Dec 11, 2018
f0bd24a
Make Room::setDisplayed() trigger loading all members
KitsuneRal Dec 11, 2018
2db8593
Merge branch 'master' into kitsune-lazy-loading
KitsuneRal Dec 11, 2018
095444a
Room::allMembersLoaded(); more doc-comments
KitsuneRal Dec 12, 2018
c46663a
qmc-example: Use lazy-loading; check full-loading upon setDisplayed
KitsuneRal Dec 12, 2018
3934855
Room: more doc-comments
KitsuneRal Dec 12, 2018
c33680b
csapi/rooms.h: regenerate to update doc-comments
KitsuneRal Dec 12, 2018
cda9a0f
gtad.yaml: use more compact definitions where possible
KitsuneRal Dec 12, 2018
91b20ca
gtad.yaml: use more compact definitions where possible
KitsuneRal Dec 12, 2018
5b06b16
gtad.yaml: wrap bool in Omittable<>
KitsuneRal Dec 12, 2018
8dcda23
Regenerate csapi/
KitsuneRal Dec 12, 2018
b4aa613
Merge branch 'kitsune-omittable-bool' into kitsune-lazy-loading
KitsuneRal Dec 12, 2018
07c9eac
Bump room state cache version to reset the cache
KitsuneRal Dec 12, 2018
cb5f0f6
Connection: initialize lazyLoading member variable
KitsuneRal Dec 13, 2018
cf4759e
qmc-example: Fix the lazy-loading test
KitsuneRal Dec 13, 2018
2cbb053
Room::getAllMembers: fix off-by-one error
KitsuneRal Dec 13, 2018
12a0b95
qmc-example: clearer QMC_CHECK; start tests only after the first sync…
KitsuneRal Dec 14, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 83 additions & 43 deletions examples/qmc-example.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,13 @@ using namespace std::placeholders;
class QMCTest : public QObject
{
public:
QMCTest(Connection* conn, const QString& testRoomName, QString source);
QMCTest(Connection* conn, QString testRoomName, QString source);

private slots:
void setup(const QString& testRoomName);
void setup();
void onNewRoom(Room* r);
void startTests();
void loadMembers();
void sendMessage();
void addAndRemoveTag();
void sendAndRedact();
Expand All @@ -43,45 +44,56 @@ class QMCTest : public QObject
QStringList succeeded;
QStringList failed;
QString origin;
QString testRoomName;
Room* targetRoom = nullptr;
};

#define QMC_CHECK(description, condition) \
{ \
const bool result = !!(condition); \
Q_ASSERT(running.removeOne(description)); \
(result ? succeeded : failed).push_back(description); \
cout << (description) << (result ? " successul" : " FAILED") << endl; \
if (targetRoom) \
targetRoom->postMessage(origin % ": " % QStringLiteral(description) % \
(result ? QStringLiteral(" successful") : QStringLiteral(" FAILED")), \
result ? MessageEventType::Notice : MessageEventType::Text); \
if (!!(condition)) \
{ \
succeeded.push_back(description); \
cout << (description) << " successful" << endl; \
if (targetRoom) \
targetRoom->postMessage( \
origin % ": " % (description) % " successful", \
MessageEventType::Notice); \
} else { \
failed.push_back(description); \
cout << (description) << " FAILED" << endl; \
if (targetRoom) \
targetRoom->postPlainText( \
origin % ": " % (description) % " FAILED"); \
} \
}

QMCTest::QMCTest(Connection* conn, const QString& testRoomName, QString source)
: c(conn), origin(std::move(source))
QMCTest::QMCTest(Connection* conn, QString testRoomName, QString source)
: c(conn), origin(std::move(source)), testRoomName(std::move(testRoomName))
{
if (!origin.isEmpty())
cout << "Origin for the test message: " << origin.toStdString() << endl;
if (!testRoomName.isEmpty())
cout << "Test room name: " << testRoomName.toStdString() << endl;

connect(c.data(), &Connection::connected,
this, std::bind(&QMCTest::setup, this, testRoomName));
connect(c.data(), &Connection::connected, this, &QMCTest::setup);
connect(c.data(), &Connection::loadedRoomState, this, &QMCTest::onNewRoom);
// Big countdown watchdog
QTimer::singleShot(180000, this, &QMCTest::leave);
}

void QMCTest::setup(const QString& testRoomName)
void QMCTest::setup()
{
cout << "Connected, server: "
<< c->homeserver().toDisplayString().toStdString() << endl;
cout << "Access token: " << c->accessToken().toStdString() << endl;

// Setting up sync loop
c->setLazyLoading(true);
c->sync();
connect(c.data(), &Connection::syncDone, c.data(), [this,testRoomName] {
connectSingleShot(c.data(), &Connection::syncDone,
this, &QMCTest::startTests);
connect(c.data(), &Connection::syncDone, c.data(), [this] {
cout << "Sync complete, "
<< running.size() << " tests in the air" << endl;
if (!running.isEmpty())
Expand All @@ -94,29 +106,6 @@ void QMCTest::setup(const QString& testRoomName)
else
finalize();
});

// Join a testroom, if provided
if (!targetRoom && !testRoomName.isEmpty())
{
cout << "Joining " << testRoomName.toStdString() << endl;
running.push_back("Join room");
auto joinJob = c->joinRoom(testRoomName);
connect(joinJob, &BaseJob::failure, this,
[this] { QMC_CHECK("Join room", false); finalize(); });
// As of BaseJob::success, a Room object is not guaranteed to even
// exist; it's a mere confirmation that the server processed
// the request.
connect(c.data(), &Connection::loadedRoomState, this,
[this,testRoomName] (Room* room) {
Q_ASSERT(room); // It's a grave failure if room is nullptr here
if (room->canonicalAlias() != testRoomName)
return; // Not our room

targetRoom = room;
QMC_CHECK("Join room", true);
startTests();
});
}
}

void QMCTest::onNewRoom(Room* r)
Expand All @@ -141,11 +130,62 @@ void QMCTest::onNewRoom(Room* r)

void QMCTest::startTests()
{
cout << "Starting tests" << endl;
sendMessage();
addAndRemoveTag();
sendAndRedact();
markDirectChat();
if (testRoomName.isEmpty())
return;

cout << "Joining " << testRoomName.toStdString() << endl;
running.push_back("Join room");
auto joinJob = c->joinRoom(testRoomName);
connect(joinJob, &BaseJob::failure, this,
[this] { QMC_CHECK("Join room", false); finalize(); });
// As of BaseJob::success, a Room object is not guaranteed to even
// exist; it's a mere confirmation that the server processed
// the request.
connect(c.data(), &Connection::loadedRoomState, this,
[this] (Room* room) {
Q_ASSERT(room); // It's a grave failure if room is nullptr here
if (room->canonicalAlias() != testRoomName)
return; // Not our room

targetRoom = room;
QMC_CHECK("Join room", true);
cout << "Starting tests" << endl;

loadMembers();
sendMessage();
addAndRemoveTag();
sendAndRedact();
markDirectChat();
});
}

void QMCTest::loadMembers()
{
running.push_back("Loading members");
// The dedicated qmc-test room is too small to test
// lazy-loading-then-full-loading; use #test:matrix.org instead.
// TODO: #264
auto* r = c->room(QStringLiteral("!vfFxDRtZSSdspfTSEr:matrix.org"));
if (!r)
{
cout << "#test:matrix.org is not found in the test user's rooms" << endl;
QMC_CHECK("Loading members", false);
return;
}
// It's not exactly correct because an arbitrary server might not support
// lazy loading; but in the absence of capabilities framework we assume
// it does.
if (r->memberNames().size() >= r->joinedCount())
{
cout << "Lazy loading doesn't seem to be enabled" << endl;
QMC_CHECK("Loading members", false);
return;
}
r->setDisplayed();
connect(r, &Room::allMembersLoaded, [this,r] {
QMC_CHECK("Loading members",
r->memberNames().size() + 1 >= r->joinedCount());
});
}

void QMCTest::sendMessage()
Expand Down
20 changes: 7 additions & 13 deletions lib/application-service/definitions/location.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,19 @@

using namespace QMatrixClient;

QJsonObject QMatrixClient::toJson(const ThirdPartyLocation& pod)
void JsonObjectConverter<ThirdPartyLocation>::dumpTo(
QJsonObject& jo, const ThirdPartyLocation& pod)
{
QJsonObject jo;
addParam<>(jo, QStringLiteral("alias"), pod.alias);
addParam<>(jo, QStringLiteral("protocol"), pod.protocol);
addParam<>(jo, QStringLiteral("fields"), pod.fields);
return jo;
}

ThirdPartyLocation FromJsonObject<ThirdPartyLocation>::operator()(const QJsonObject& jo) const
void JsonObjectConverter<ThirdPartyLocation>::fillFrom(
const QJsonObject& jo, ThirdPartyLocation& result)
{
ThirdPartyLocation result;
result.alias =
fromJson<QString>(jo.value("alias"_ls));
result.protocol =
fromJson<QString>(jo.value("protocol"_ls));
result.fields =
fromJson<QJsonObject>(jo.value("fields"_ls));

return result;
fromJson(jo.value("alias"_ls), result.alias);
fromJson(jo.value("protocol"_ls), result.protocol);
fromJson(jo.value("fields"_ls), result.fields);
}

8 changes: 3 additions & 5 deletions lib/application-service/definitions/location.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,10 @@ namespace QMatrixClient
/// Information used to identify this third party location.
QJsonObject fields;
};

QJsonObject toJson(const ThirdPartyLocation& pod);

template <> struct FromJsonObject<ThirdPartyLocation>
template <> struct JsonObjectConverter<ThirdPartyLocation>
{
ThirdPartyLocation operator()(const QJsonObject& jo) const;
static void dumpTo(QJsonObject& jo, const ThirdPartyLocation& pod);
static void fillFrom(const QJsonObject& jo, ThirdPartyLocation& pod);
};

} // namespace QMatrixClient
66 changes: 23 additions & 43 deletions lib/application-service/definitions/protocol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,75 +6,55 @@

using namespace QMatrixClient;

QJsonObject QMatrixClient::toJson(const FieldType& pod)
void JsonObjectConverter<FieldType>::dumpTo(
QJsonObject& jo, const FieldType& pod)
{
QJsonObject jo;
addParam<>(jo, QStringLiteral("regexp"), pod.regexp);
addParam<>(jo, QStringLiteral("placeholder"), pod.placeholder);
return jo;
}

FieldType FromJsonObject<FieldType>::operator()(const QJsonObject& jo) const
void JsonObjectConverter<FieldType>::fillFrom(
const QJsonObject& jo, FieldType& result)
{
FieldType result;
result.regexp =
fromJson<QString>(jo.value("regexp"_ls));
result.placeholder =
fromJson<QString>(jo.value("placeholder"_ls));

return result;
fromJson(jo.value("regexp"_ls), result.regexp);
fromJson(jo.value("placeholder"_ls), result.placeholder);
}

QJsonObject QMatrixClient::toJson(const ProtocolInstance& pod)
void JsonObjectConverter<ProtocolInstance>::dumpTo(
QJsonObject& jo, const ProtocolInstance& pod)
{
QJsonObject jo;
addParam<>(jo, QStringLiteral("desc"), pod.desc);
addParam<IfNotEmpty>(jo, QStringLiteral("icon"), pod.icon);
addParam<>(jo, QStringLiteral("fields"), pod.fields);
addParam<>(jo, QStringLiteral("network_id"), pod.networkId);
return jo;
}

ProtocolInstance FromJsonObject<ProtocolInstance>::operator()(const QJsonObject& jo) const
void JsonObjectConverter<ProtocolInstance>::fillFrom(
const QJsonObject& jo, ProtocolInstance& result)
{
ProtocolInstance result;
result.desc =
fromJson<QString>(jo.value("desc"_ls));
result.icon =
fromJson<QString>(jo.value("icon"_ls));
result.fields =
fromJson<QJsonObject>(jo.value("fields"_ls));
result.networkId =
fromJson<QString>(jo.value("network_id"_ls));

return result;
fromJson(jo.value("desc"_ls), result.desc);
fromJson(jo.value("icon"_ls), result.icon);
fromJson(jo.value("fields"_ls), result.fields);
fromJson(jo.value("network_id"_ls), result.networkId);
}

QJsonObject QMatrixClient::toJson(const ThirdPartyProtocol& pod)
void JsonObjectConverter<ThirdPartyProtocol>::dumpTo(
QJsonObject& jo, const ThirdPartyProtocol& pod)
{
QJsonObject jo;
addParam<>(jo, QStringLiteral("user_fields"), pod.userFields);
addParam<>(jo, QStringLiteral("location_fields"), pod.locationFields);
addParam<>(jo, QStringLiteral("icon"), pod.icon);
addParam<>(jo, QStringLiteral("field_types"), pod.fieldTypes);
addParam<>(jo, QStringLiteral("instances"), pod.instances);
return jo;
}

ThirdPartyProtocol FromJsonObject<ThirdPartyProtocol>::operator()(const QJsonObject& jo) const
void JsonObjectConverter<ThirdPartyProtocol>::fillFrom(
const QJsonObject& jo, ThirdPartyProtocol& result)
{
ThirdPartyProtocol result;
result.userFields =
fromJson<QStringList>(jo.value("user_fields"_ls));
result.locationFields =
fromJson<QStringList>(jo.value("location_fields"_ls));
result.icon =
fromJson<QString>(jo.value("icon"_ls));
result.fieldTypes =
fromJson<QHash<QString, FieldType>>(jo.value("field_types"_ls));
result.instances =
fromJson<QVector<ProtocolInstance>>(jo.value("instances"_ls));

return result;
fromJson(jo.value("user_fields"_ls), result.userFields);
fromJson(jo.value("location_fields"_ls), result.locationFields);
fromJson(jo.value("icon"_ls), result.icon);
fromJson(jo.value("field_types"_ls), result.fieldTypes);
fromJson(jo.value("instances"_ls), result.instances);
}

24 changes: 9 additions & 15 deletions lib/application-service/definitions/protocol.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,10 @@ namespace QMatrixClient
/// An placeholder serving as a valid example of the field value.
QString placeholder;
};

QJsonObject toJson(const FieldType& pod);

template <> struct FromJsonObject<FieldType>
template <> struct JsonObjectConverter<FieldType>
{
FieldType operator()(const QJsonObject& jo) const;
static void dumpTo(QJsonObject& jo, const FieldType& pod);
static void fillFrom(const QJsonObject& jo, FieldType& pod);
};

struct ProtocolInstance
Expand All @@ -45,12 +43,10 @@ namespace QMatrixClient
/// A unique identifier across all instances.
QString networkId;
};

QJsonObject toJson(const ProtocolInstance& pod);

template <> struct FromJsonObject<ProtocolInstance>
template <> struct JsonObjectConverter<ProtocolInstance>
{
ProtocolInstance operator()(const QJsonObject& jo) const;
static void dumpTo(QJsonObject& jo, const ProtocolInstance& pod);
static void fillFrom(const QJsonObject& jo, ProtocolInstance& pod);
};

struct ThirdPartyProtocol
Expand Down Expand Up @@ -78,12 +74,10 @@ namespace QMatrixClient
/// same application service.
QVector<ProtocolInstance> instances;
};

QJsonObject toJson(const ThirdPartyProtocol& pod);

template <> struct FromJsonObject<ThirdPartyProtocol>
template <> struct JsonObjectConverter<ThirdPartyProtocol>
{
ThirdPartyProtocol operator()(const QJsonObject& jo) const;
static void dumpTo(QJsonObject& jo, const ThirdPartyProtocol& pod);
static void fillFrom(const QJsonObject& jo, ThirdPartyProtocol& pod);
};

} // namespace QMatrixClient
Loading