Akonadi

relationmodifyhandler.cpp
1/*
2 SPDX-FileCopyrightText: 2014 Christian Mollekopf <mollekopf@kolabsys.com>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
7#include "relationmodifyhandler.h"
8
9#include "connection.h"
10#include "storage/datastore.h"
11#include "storage/querybuilder.h"
12#include "storage/selectquerybuilder.h"
13
14using namespace Akonadi;
15using namespace Akonadi::Server;
16
17RelationModifyHandler::RelationModifyHandler(AkonadiServer &akonadi)
18 : Handler(akonadi)
19{
20}
21
22Relation RelationModifyHandler::fetchRelation(qint64 leftId, qint64 rightId, qint64 typeId)
23{
24 SelectQueryBuilder<Relation> relationQuery;
25 relationQuery.addValueCondition(Relation::leftIdFullColumnName(), Query::Equals, leftId);
26 relationQuery.addValueCondition(Relation::rightIdFullColumnName(), Query::Equals, rightId);
27 relationQuery.addValueCondition(Relation::typeIdFullColumnName(), Query::Equals, typeId);
28 if (!relationQuery.exec()) {
29 throw HandlerException("Failed to query for existing relation");
30 }
31 const Relation::List existingRelations = relationQuery.result();
32 if (!existingRelations.isEmpty()) {
33 if (existingRelations.size() == 1) {
34 return existingRelations.at(0);
35 } else {
36 throw HandlerException("Matched more than 1 relation");
37 }
38 }
39
40 return Relation();
41}
42
43bool RelationModifyHandler::parseStream()
44{
45 const auto &cmd = Protocol::cmdCast<Protocol::ModifyRelationCommand>(m_command);
46
47 if (cmd.type().isEmpty()) {
48 return failureResponse("Relation type not specified");
49 }
50
51 if (cmd.left() < 0 || cmd.right() < 0) {
52 return failureResponse("Invalid relation specified");
53 }
54
55 if (!cmd.remoteId().isEmpty() && !connection()->context().resource().isValid()) {
56 return failureResponse("RemoteID can only be set by Resources");
57 }
58
59 const QString typeName = QString::fromUtf8(cmd.type());
60 const RelationType relationType = RelationType::retrieveByNameOrCreate(typeName);
61 if (!relationType.isValid()) {
62 return failureResponse(QStringLiteral("Unable to create relation type '") % typeName % QStringLiteral("'"));
63 }
64
65 Relation existingRelation = fetchRelation(cmd.left(), cmd.right(), relationType.id());
66 if (existingRelation.isValid()) {
67 existingRelation.setRemoteId(QLatin1StringView(cmd.remoteId()));
68 if (!existingRelation.update()) {
69 return failureResponse("Failed to update relation");
70 }
71 }
72
73 // Can't use insert(), does not work here (no "id" column)
74 QueryBuilder inQb(Relation::tableName(), QueryBuilder::Insert);
75 inQb.setIdentificationColumn(QString()); // omit "RETURNING xyz" with PSQL
76 inQb.setColumnValue(Relation::leftIdColumn(), cmd.left());
77 inQb.setColumnValue(Relation::rightIdColumn(), cmd.right());
78 inQb.setColumnValue(Relation::typeIdColumn(), relationType.id());
79 if (!inQb.exec()) {
80 throw HandlerException("Failed to store relation");
81 }
82
83 Relation insertedRelation = fetchRelation(cmd.left(), cmd.right(), relationType.id());
84
85 // Get all PIM items that are part of the relation
87 itemsQuery.setSubQueryMode(Query::Or);
88 itemsQuery.addValueCondition(PimItem::idColumn(), Query::Equals, cmd.left());
89 itemsQuery.addValueCondition(PimItem::idColumn(), Query::Equals, cmd.right());
90
91 if (!itemsQuery.exec()) {
92 return failureResponse("Adding relation failed");
93 }
94 const PimItem::List items = itemsQuery.result();
95
96 if (items.size() != 2) {
97 return failureResponse("Couldn't find items for relation");
98 }
99
100 /* if (items[0].collection().resourceId() != items[1].collection().resourceId()) {
101 throw HandlerException("Relations can only be created for items within the same resource");
102 } */
103
104 auto collector = storageBackend()->notificationCollector();
105 collector->relationAdded(insertedRelation);
106 collector->itemsRelationsChanged(items, {insertedRelation}, {});
107
108 return successResponse<Protocol::ModifyRelationResponse>();
109}
An Akonadi Relation.
Definition relation.h:41
void setRemoteId(const QByteArray &type)
Sets the remote id of the relation.
Definition relation.cpp:90
NotificationCollector * notificationCollector()
Returns the notification collector of this DataStore object.
The handler interfaces describes an entity capable of handling an AkonadiIMAP command.
Definition handler.h:32
void relationAdded(const Relation &relation)
Notify about an added relation.
Helper class to construct arbitrary SQL queries.
void addValueCondition(const QString &column, Query::CompareOperator op, const QVariant &value, ConditionType type=WhereCondition)
Add a WHERE or HAVING condition which compares a column with a given value.
bool exec()
Executes the query, returns true on success.
void setSubQueryMode(Query::LogicOperator op, ConditionType type=WhereCondition)
Define how WHERE or HAVING conditions are combined.
Helper class for creating and executing database SELECT queries.
QList< T > result()
Returns the result of this SELECT query.
Helper integration between Akonadi and Qt.
QString typeName(const QJsonObject &obj)
bool isValid(QStringView ifopt)
const_reference at(qsizetype i) const const
bool isEmpty() const const
qsizetype size() const const
QString fromUtf8(QByteArrayView str)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:13:38 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.