Akonadi

relationmodifyhandler.cpp
1 /*
2  Copyright (c) 2014 Christian Mollekopf <[email protected]>
3 
4  This library is free software; you can redistribute it and/or modify it
5  under the terms of the GNU Library General Public License as published by
6  the Free Software Foundation; either version 2 of the License, or (at your
7  option) any later version.
8 
9  This library is distributed in the hope that it will be useful, but WITHOUT
10  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
12  License for more details.
13 
14  You should have received a copy of the GNU Library General Public License
15  along with this library; see the file COPYING.LIB. If not, write to the
16  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17  02110-1301, USA.
18 */
19 
20 #include "relationmodifyhandler.h"
21 
22 #include "connection.h"
23 #include "storage/datastore.h"
24 #include "storage/querybuilder.h"
25 #include "storage/selectquerybuilder.h"
26 
27 using namespace Akonadi;
28 using namespace Akonadi::Server;
29 
30 RelationModifyHandler::RelationModifyHandler(AkonadiServer &akonadi)
31  : Handler(akonadi)
32 {}
33 
34 Relation RelationModifyHandler::fetchRelation(qint64 leftId, qint64 rightId, qint64 typeId)
35 {
36  SelectQueryBuilder<Relation> relationQuery;
37  relationQuery.addValueCondition(Relation::leftIdFullColumnName(), Query::Equals, leftId);
38  relationQuery.addValueCondition(Relation::rightIdFullColumnName(), Query::Equals, rightId);
39  relationQuery.addValueCondition(Relation::typeIdFullColumnName(), Query::Equals, typeId);
40  if (!relationQuery.exec()) {
41  throw HandlerException("Failed to query for existing relation");
42  }
43  const Relation::List existingRelations = relationQuery.result();
44  if (!existingRelations.isEmpty()) {
45  if (existingRelations.size() == 1) {
46  return existingRelations.at(0);
47  } else {
48  throw HandlerException("Matched more than 1 relation");
49  }
50  }
51 
52  return Relation();
53 }
54 
55 bool RelationModifyHandler::parseStream()
56 {
57  const auto &cmd = Protocol::cmdCast<Protocol::ModifyRelationCommand>(m_command);
58 
59  if (cmd.type().isEmpty()) {
60  return failureResponse("Relation type not specified");
61  }
62 
63  if (cmd.left() < 0 || cmd.right() < 0) {
64  return failureResponse("Invalid relation specified");
65  }
66 
67  if (!cmd.remoteId().isEmpty() && !connection()->context().resource().isValid()) {
68  return failureResponse("RemoteID can only be set by Resources");
69  }
70 
71  const QString typeName = QString::fromUtf8(cmd.type());
72  const RelationType relationType = RelationType::retrieveByNameOrCreate(typeName);
73  if (!relationType.isValid()) {
74  return failureResponse(QStringLiteral("Unable to create relation type '") % typeName % QStringLiteral("'"));
75  }
76 
77  Relation existingRelation = fetchRelation(cmd.left(), cmd.right(), relationType.id());
78  if (existingRelation.isValid()) {
79  existingRelation.setRemoteId(QLatin1String(cmd.remoteId()));
80  if (!existingRelation.update()) {
81  return failureResponse("Failed to update relation");
82  }
83  }
84 
85  // Can't use insert(), does not work here (no "id" column)
86  QueryBuilder inQb(Relation::tableName(), QueryBuilder::Insert);
87  inQb.setIdentificationColumn(QString()); // omit "RETURING xyz" with PSQL
88  inQb.setColumnValue(Relation::leftIdColumn(), cmd.left());
89  inQb.setColumnValue(Relation::rightIdColumn(), cmd.right());
90  inQb.setColumnValue(Relation::typeIdColumn(), relationType.id());
91  if (!inQb.exec()) {
92  throw HandlerException("Failed to store relation");
93  }
94 
95  Relation insertedRelation = fetchRelation(cmd.left(), cmd.right(), relationType.id());
96 
97  // Get all PIM items that are part of the relation
98  SelectQueryBuilder<PimItem> itemsQuery;
99  itemsQuery.setSubQueryMode(Query::Or);
100  itemsQuery.addValueCondition(PimItem::idColumn(), Query::Equals, cmd.left());
101  itemsQuery.addValueCondition(PimItem::idColumn(), Query::Equals, cmd.right());
102 
103  if (!itemsQuery.exec()) {
104  return failureResponse("Adding relation failed");
105  }
106  const PimItem::List items = itemsQuery.result();
107 
108  if (items.size() != 2) {
109  return failureResponse("Couldn't find items for relation");
110  }
111 
112  /* if (items[0].collection().resourceId() != items[1].collection().resourceId()) {
113  throw HandlerException("Relations can only be created for items within the same resource");
114  } */
115 
116  auto collector = storageBackend()->notificationCollector();
117  collector->relationAdded(insertedRelation);
118  collector->itemsRelationsChanged(items, {insertedRelation}, {});
119 
120  return successResponse<Protocol::ModifyRelationResponse>();
121 }
122 
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.
An Akonadi Relation.
Definition: relation.h:52
The handler interfaces describes an entity capable of handling an AkonadiIMAP command.
Definition: handler.h:48
void setSubQueryMode(Query::LogicOperator op, ConditionType type=WhereCondition)
Define how WHERE or HAVING conditions are combined.
void setRemoteId(const QByteArray &type)
Sets the remote id of the relation.
Definition: relation.cpp:104
QString fromUtf8(const char *str, int size)
QVector< T > result()
Returns the result of this SELECT query.
const T & at(int i) const const
Helper class for creating and executing database SELECT queries.
bool isEmpty() const const
Helper integration between Akonadi and Qt.
int size() const const
Helper class to construct arbitrary SQL queries.
Definition: querybuilder.h:45
bool exec()
Executes the query, returns true on success.
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Fri Jun 5 2020 23:08:55 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.