Skip to content

Commit

Permalink
Add deletion
Browse files Browse the repository at this point in the history
  • Loading branch information
rjzondervan committed Jan 9, 2025
1 parent 13f08b8 commit 334688c
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 17 deletions.
4 changes: 2 additions & 2 deletions lib/AppInfo/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ public function register(IRegistrationContext $context): void {

/* @var IEventDispatcher $dispatcher */
$dispatcher = $this->getContainer()->get(IEventDispatcher::class);
$dispatcher->addServiceListener(eventName: ObjectUpdatedEvent::class, className: ObjectCreatedEventListener::class);
$dispatcher->addServiceListener(eventName: ObjectCreatedEvent::class, className: ObjectUpdatedEventListener::class);
$dispatcher->addServiceListener(eventName: ObjectCreatedEvent::class, className: ObjectCreatedEventListener::class);
$dispatcher->addServiceListener(eventName: ObjectUpdatedEvent::class, className: ObjectUpdatedEventListener::class);
$dispatcher->addServiceListener(eventName: ObjectDeletedEvent::class, className: ObjectDeletedEventListener::class);
}

Expand Down
10 changes: 6 additions & 4 deletions lib/Db/SynchronizationContractMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ public function updateFromArray(int $id, array $object): SynchronizationContract
*
* @return SynchronizationContract|null The matching contract or null if not found.
*/
public function findByOriginId(string $originId): ?SynchronizationContract
public function findByOriginId(?string $originId): ?SynchronizationContract
{
// Create query builder
$qb = $this->db->getQueryBuilder();
Expand Down Expand Up @@ -334,7 +334,7 @@ public function getTotalCallCount(): int

/**
* Handle object removal by updating or removing associated contracts
*
*
* This method finds all contracts associated with the given object identifier,
* clears the appropriate fields (origin or target) and deletes contracts that
* have no remaining associations.
Expand All @@ -343,7 +343,7 @@ public function getTotalCallCount(): int
* @return void
* @throws Exception If there is an error handling the object removal
*/
public function handleObjectRemoval(string $objectIdentifier): void
public function handleObjectRemoval(string $objectIdentifier): array
{
try {
// Find contracts where object ID matches either origin or target
Expand All @@ -367,7 +367,7 @@ public function handleObjectRemoval(string $objectIdentifier): void
$this->update($contract);
}

// Clear target fields if object was the target
// Clear target fields if object was the target
if ($contract->getTargetId() === $objectIdentifier) {
$contract->setTargetId(null);
$contract->setTargetHash(null);
Expand All @@ -379,6 +379,8 @@ public function handleObjectRemoval(string $objectIdentifier): void
$this->delete($contract);
}
}
return $contracts;

} catch (Exception $e) {
throw new Exception('Failed to handle object removal: ' . $e->getMessage());
}
Expand Down
1 change: 1 addition & 0 deletions lib/EventListener/ObjectCreatedEventListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public function __construct(
*/
public function handle(Event $event): void
{

if ($event instanceof ObjectCreatedEvent === false)
{
return;
Expand Down
7 changes: 5 additions & 2 deletions lib/EventListener/ObjectDeletedEventListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,11 @@ public function handle(Event $event): void

$object = $event->getObject();

$this->synchronizationService->removeObjectFromTarget($object);
$this->synchronizationContractMapper->handleObjectRemoval($object->getId());
$contracts = $this->synchronizationContractMapper->handleObjectRemoval($object->getUuid());

foreach($contracts as $contract) {
$this->synchronizationService->synchronizeToTarget($object, $contract);
}

}
}
61 changes: 61 additions & 0 deletions lib/Migration/Version1Date20250109121103.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<?php

declare(strict_types=1);

/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

namespace OCA\OpenConnector\Migration;

use Closure;
use OCP\DB\ISchemaWrapper;
use OCP\DB\Types;
use OCP\Migration\IOutput;
use OCP\Migration\SimpleMigrationStep;

/**
* FIXME Auto-generated migration step: Please modify to your needs!
*/
class Version1Date20250109121103 extends SimpleMigrationStep {

/**
* @param IOutput $output
* @param Closure(): ISchemaWrapper $schemaClosure
* @param array $options
*/
public function preSchemaChange(IOutput $output, Closure $schemaClosure, array $options): void {
}

/**
* @param IOutput $output
* @param Closure(): ISchemaWrapper $schemaClosure
* @param array $options
* @return null|ISchemaWrapper
*/
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
/**
* @var ISchemaWrapper $schema
*/
$schema = $schemaClosure();

if($schema->hasTable(tableName: 'openconnector_synchronization_contracts') === true) {
$table = $schema->getTable(tableName: 'openconnector_synchronization_contracts');
$table->getColumn('origin_id')->setNotnull(false);
$table->getColumn('target_id')->setNotnull(false);
$table->getColumn('origin_hash')->setNotnull(false);
$table->getColumn('target_hash')->setNotnull(false);
}

return $schema;
}

/**
* @param IOutput $output
* @param Closure(): ISchemaWrapper $schemaClosure
* @param array $options
*/
public function postSchemaChange(IOutput $output, Closure $schemaClosure, array $options): void {
}
}
36 changes: 27 additions & 9 deletions lib/Service/SynchronizationService.php
Original file line number Diff line number Diff line change
Expand Up @@ -510,16 +510,16 @@ public function synchronizeContract(SynchronizationContract $synchronizationCont
return new Exception($exception->getMessage());
}
}

// Let's prevent pointless updates by checking:
// 1. If the origin hash matches (object hasn't changed)
// 2. If the synchronization config hasn't been updated since last check
// 3. If source target mapping exists, check it hasn't been updated since last check
// 4. If target ID and hash exist (object hasn't been removed from target)
if (
$originHash === $synchronizationContract->getOriginHash() &&
$originHash === $synchronizationContract->getOriginHash() &&
$synchronization->getUpdated() < $synchronizationContract->getSourceLastChecked() &&
($sourceTargetMapping === null ||
($sourceTargetMapping === null ||
$sourceTargetMapping->getUpdated() < $synchronizationContract->getSourceLastChecked()) &&
$synchronizationContract->getTargetId() !== null &&
$synchronizationContract->getTargetHash() !== null
Expand All @@ -533,7 +533,7 @@ public function synchronizeContract(SynchronizationContract $synchronizationCont
$synchronizationContract->setSourceLastChanged(new DateTime());
$synchronizationContract->setSourceLastChecked(new DateTime());



// Execute mapping if found
if ($sourceTargetMapping) {
Expand Down Expand Up @@ -997,7 +997,7 @@ private function writeObjectToTarget(
$target = $this->sourceMapper->find(id: $synchronization->getTargetId());

$sourceId = $synchronization->getSourceId();
if ($synchronization->getSourceType() === 'register/schema') {
if ($synchronization->getSourceType() === 'register/schema' && $contract->getOriginId() !== null) {
$sourceIds = explode(separator: '/', string: $sourceId);

$this->objectService->getOpenRegisters()->setRegister($sourceIds[0]);
Expand All @@ -1010,13 +1010,27 @@ private function writeObjectToTarget(


$targetConfig = $this->callService->applyConfigDot($synchronization->getTargetConfig());
// @TODO For now only JSON APIs are supported
$targetConfig['json'] = $object;


if (str_starts_with($endpoint, $target->getLocation()) === true) {
$endpoint = str_replace(search: $target->getLocation(), replace: '', subject: $endpoint);
}

if ($contract->getOriginId() === null) {

$endpoint .= '/'.$contract->getTargetId();
$response = $this->callService->call(source: $target, endpoint: $endpoint, method: 'DELETE', config: $targetConfig)->getResponse();

$contract->setTargetHash(md5(serialize($response['body'])));
$contract->setTargetId(null);

return $contract;
}


// @TODO For now only JSON APIs are supported
$targetConfig['json'] = $object;

if ($contract->getTargetId() === null) {
$response = $this->callService->call(source: $target, endpoint: $endpoint, method: 'POST', config: $targetConfig)->getResponse();

Expand All @@ -1028,18 +1042,22 @@ private function writeObjectToTarget(
return $contract;
}

$endpoint .= '/'.$contract->getTargetId();

$response = $this->callService->call(source: $target, endpoint: $endpoint, method: 'PUT', config: $targetConfig)->getResponse();

$body = json_decode($response['body'], true);

return $contract;
}

public function synchronizeToTarget(ObjectEntity $object): array
public function synchronizeToTarget(ObjectEntity $object, ?SynchronizationContract $synchronizationContract = null): array
{
$objectId = $object->getUuid();

$synchronizationContract = $this->synchronizationContractMapper->findByOriginId($objectId);
if($synchronizationContract === null) {
$synchronizationContract = $this->synchronizationContractMapper->findByOriginId($objectId);
}

$synchronizations = $this->synchronizationMapper->findAll(filters: [
'source_type' => 'register/schema',
Expand Down

0 comments on commit 334688c

Please sign in to comment.