File indexing completed on 2024-06-23 05:07:02

0001 /*
0002     SPDX-FileCopyrightText: 2014 Christian Mollekopf <mollekopf@kolabsys.com>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "relationmodifyhandler.h"
0008 
0009 #include "connection.h"
0010 #include "storage/datastore.h"
0011 #include "storage/querybuilder.h"
0012 #include "storage/selectquerybuilder.h"
0013 
0014 using namespace Akonadi;
0015 using namespace Akonadi::Server;
0016 
0017 RelationModifyHandler::RelationModifyHandler(AkonadiServer &akonadi)
0018     : Handler(akonadi)
0019 {
0020 }
0021 
0022 Relation RelationModifyHandler::fetchRelation(qint64 leftId, qint64 rightId, qint64 typeId)
0023 {
0024     SelectQueryBuilder<Relation> relationQuery;
0025     relationQuery.addValueCondition(Relation::leftIdFullColumnName(), Query::Equals, leftId);
0026     relationQuery.addValueCondition(Relation::rightIdFullColumnName(), Query::Equals, rightId);
0027     relationQuery.addValueCondition(Relation::typeIdFullColumnName(), Query::Equals, typeId);
0028     if (!relationQuery.exec()) {
0029         throw HandlerException("Failed to query for existing relation");
0030     }
0031     const Relation::List existingRelations = relationQuery.result();
0032     if (!existingRelations.isEmpty()) {
0033         if (existingRelations.size() == 1) {
0034             return existingRelations.at(0);
0035         } else {
0036             throw HandlerException("Matched more than 1 relation");
0037         }
0038     }
0039 
0040     return Relation();
0041 }
0042 
0043 bool RelationModifyHandler::parseStream()
0044 {
0045     const auto &cmd = Protocol::cmdCast<Protocol::ModifyRelationCommand>(m_command);
0046 
0047     if (cmd.type().isEmpty()) {
0048         return failureResponse("Relation type not specified");
0049     }
0050 
0051     if (cmd.left() < 0 || cmd.right() < 0) {
0052         return failureResponse("Invalid relation specified");
0053     }
0054 
0055     if (!cmd.remoteId().isEmpty() && !connection()->context().resource().isValid()) {
0056         return failureResponse("RemoteID can only be set by Resources");
0057     }
0058 
0059     const QString typeName = QString::fromUtf8(cmd.type());
0060     const RelationType relationType = RelationType::retrieveByNameOrCreate(typeName);
0061     if (!relationType.isValid()) {
0062         return failureResponse(QStringLiteral("Unable to create relation type '") % typeName % QStringLiteral("'"));
0063     }
0064 
0065     Relation existingRelation = fetchRelation(cmd.left(), cmd.right(), relationType.id());
0066     if (existingRelation.isValid()) {
0067         existingRelation.setRemoteId(QLatin1StringView(cmd.remoteId()));
0068         if (!existingRelation.update()) {
0069             return failureResponse("Failed to update relation");
0070         }
0071     }
0072 
0073     // Can't use insert(), does not work here (no "id" column)
0074     QueryBuilder inQb(Relation::tableName(), QueryBuilder::Insert);
0075     inQb.setIdentificationColumn(QString()); // omit "RETURNING xyz" with PSQL
0076     inQb.setColumnValue(Relation::leftIdColumn(), cmd.left());
0077     inQb.setColumnValue(Relation::rightIdColumn(), cmd.right());
0078     inQb.setColumnValue(Relation::typeIdColumn(), relationType.id());
0079     if (!inQb.exec()) {
0080         throw HandlerException("Failed to store relation");
0081     }
0082 
0083     Relation insertedRelation = fetchRelation(cmd.left(), cmd.right(), relationType.id());
0084 
0085     // Get all PIM items that are part of the relation
0086     SelectQueryBuilder<PimItem> itemsQuery;
0087     itemsQuery.setSubQueryMode(Query::Or);
0088     itemsQuery.addValueCondition(PimItem::idColumn(), Query::Equals, cmd.left());
0089     itemsQuery.addValueCondition(PimItem::idColumn(), Query::Equals, cmd.right());
0090 
0091     if (!itemsQuery.exec()) {
0092         return failureResponse("Adding relation failed");
0093     }
0094     const PimItem::List items = itemsQuery.result();
0095 
0096     if (items.size() != 2) {
0097         return failureResponse("Couldn't find items for relation");
0098     }
0099 
0100     /* if (items[0].collection().resourceId() != items[1].collection().resourceId()) {
0101         throw HandlerException("Relations can only be created for items within the same resource");
0102     } */
0103 
0104     auto collector = storageBackend()->notificationCollector();
0105     collector->relationAdded(insertedRelation);
0106     collector->itemsRelationsChanged(items, {insertedRelation}, {});
0107 
0108     return successResponse<Protocol::ModifyRelationResponse>();
0109 }