Akonadi

relationmodifyhandler.cpp
1 /*
2  SPDX-FileCopyrightText: 2014 Christian Mollekopf <[email protected]>
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 
14 using namespace Akonadi;
15 using namespace Akonadi::Server;
16 
17 RelationModifyHandler::RelationModifyHandler(AkonadiServer &akonadi)
18  : Handler(akonadi)
19 {
20 }
21 
22 Relation 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 
43 bool 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(QLatin1String(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
86  SelectQueryBuilder<PimItem> itemsQuery;
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 }
bool isEmpty() const const
QString fromUtf8(const char *str, int size)
An Akonadi Relation.
Definition: relation.h:39
void setRemoteId(const QByteArray &type)
Sets the remote id of the relation.
Definition: relation.cpp:90
const T & at(int i) const const
QVector< T > result()
Returns the result of this SELECT query.
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.
Helper class for creating and executing database SELECT queries.
The handler interfaces describes an entity capable of handling an AkonadiIMAP command.
Definition: handler.h:39
bool isValid(QStringView ifopt)
void setSubQueryMode(Query::LogicOperator op, ConditionType type=WhereCondition)
Define how WHERE or HAVING conditions are combined.
int size() const const
Helper class to construct arbitrary SQL queries.
Definition: querybuilder.h:31
Helper integration between Akonadi and Qt.
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Mon Jun 27 2022 04:01:07 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.