Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

many bug fixes #179

Open
wants to merge 32 commits into
base: development
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
39400be
Merge pull request #139 from ConductionNL/development
rjzondervan Dec 19, 2024
5255ee6
Bump version to 0.1.30
actions-user Dec 19, 2024
8b78c89
Merge pull request #144 from ConductionNL/development
remko48 Dec 31, 2024
19500f2
Bump version to 0.1.31
actions-user Dec 31, 2024
9e9f6a6
Merge pull request #150 from ConductionNL/development
bbrands02 Jan 6, 2025
d8d38dd
Bump version to 0.1.32
actions-user Jan 6, 2025
2c8ed08
Merge pull request #166 from ConductionNL/development
WilcoLouwerse Jan 10, 2025
c29affc
Bump version to 0.1.33
actions-user Jan 10, 2025
81c9555
Merge pull request #171 from ConductionNL/development
rubenvdlinde Jan 14, 2025
2425267
Bump version to 0.1.34
actions-user Jan 14, 2025
8125042
Merge pull request #175 from ConductionNL/development
remko48 Jan 15, 2025
ec7d2f4
Bump version to 0.1.35
actions-user Jan 15, 2025
5154bb3
Mapping hotfix
rubenvdlinde Jan 15, 2025
baeff9d
Merge branch 'feature/CONNECTOR-52/cloud-events' of https://github.co…
rubenvdlinde Jan 15, 2025
420bd9b
Merge pull request #177 from ConductionNL/hotfix/mapping
rubenvdlinde Jan 15, 2025
0e0c2fb
Bump version to 0.1.36
actions-user Jan 15, 2025
0421494
many bug fixes
SudoThijn Jan 15, 2025
8172adf
Merge pull request #181 from ConductionNL/development
bbrands02 Jan 16, 2025
6b337b8
Bump version to 0.1.37
actions-user Jan 16, 2025
895d5e9
Merge pull request #184 from ConductionNL/development
rjzondervan Jan 16, 2025
2aa83ea
Bump version to 0.1.38
actions-user Jan 16, 2025
a6c30b1
Doc updates
rubenvdlinde Jan 17, 2025
8e2f93b
Add forcing tho synchronysation service
rubenvdlinde Jan 18, 2025
0e902d0
Updated the synchronysation docs for force
rubenvdlinde Jan 18, 2025
c0df14c
More clearity in the login of synchronysation results
rubenvdlinde Jan 18, 2025
e71df45
Merge tag 'v0.1.38' into feature/CONNECTOR-155/action-menu-same
rubenvdlinde Jan 18, 2025
afbcb63
First testing
rubenvdlinde Jan 19, 2025
6e4c452
Bit more error fixing
rubenvdlinde Jan 20, 2025
e817663
Attaching th contract log to the sync log
rubenvdlinde Jan 20, 2025
2b5a498
Bit of code cleanup and a backward compatibilty fix
rubenvdlinde Jan 20, 2025
5e78932
Fixing the ui
rubenvdlinde Jan 20, 2025
ce0bd46
Rout fixes for actually displaying the logs
rubenvdlinde Jan 20, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion appinfo/info.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ The OpenConnector Nextcloud app provides a ESB-framework to work together in an
- 🆓 Map and translate API calls

]]></description>
<version>0.1.29</version>
<version>0.1.38</version>
<licence>agpl</licence>
<category>integration</category>
<author mail="[email protected]" homepage="https://www.conduction.nl/">Conduction</author>
Expand Down
2 changes: 1 addition & 1 deletion appinfo/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

// Synchronization endpoints
['name' => 'synchronizations#contracts', 'url' => '/api/synchronizations-contracts/{id}', 'verb' => 'GET'],
['name' => 'synchronizations#logs', 'url' => '/api/synchronizations-logs/{id}', 'verb' => 'GET'],
['name' => 'synchronizations#logs', 'url' => '/api/synchronizations-logs/{uuid}', 'verb' => 'GET'],
['name' => 'synchronizations#test', 'url' => '/api/synchronizations-test/{id}', 'verb' => 'POST'],
['name' => 'synchronizations#run', 'url' => '/api/synchronizations-run/{id}', 'verb' => 'POST'],

Expand Down
17 changes: 15 additions & 2 deletions docs/Synchronization/Synchronization.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,27 @@ Synchronization is a core feature that enables data transfer between different s
- Logs synchronization events and errors

## Process Flow
1. Fetch objects from source system with pagination
2. Filter objects based on configured conditions
1. Fetch all objects from source system with pagination
2. Determing if an object has changed
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct spelling is Determining

3. Create/update synchronization contracts for each object
4. Transform data according to mapping rules
5. Write objects to target system (POST/PUT/DELETE)
6. Update contract status and hashes
7. Handle any follow-up synchronizations

### Determining if an object has changed
The synchronization service prevents unnecessary updates and code execution by checking the following conditions:

1. The origin hash matches the stored hash in the synchronization contract (indicating the source object hasn't changed)
2. The synchronization configuration hasn't been updated since the last check (synchronization.updated < contract.sourceLastChecked)
3. If a source target mapping exists, verify it hasn't been updated since the last check (mapping.updated < contract.sourceLastChecked)
4. The target ID and hash exist in the synchronization contract (object hasn't been removed from target system)
5. The force parameter is false (if true, the update will proceed regardless of other conditions)

If all these conditions are met, the synchronization service will skip updating the object since no changes are needed. This optimization prevents unnecessary API calls and processing.

This procces might be overridden by the user by setting the force option to true. In that case the synchronization service will update the target object regardless of whether it has changed or not.

## Error Handling
- Rate limiting detection and backoff
- Logging of failed operations
Expand Down
4 changes: 4 additions & 0 deletions docs/source/source.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Source

## Configuguration
Here you can configure the calls to the source. -> https://docs.guzzlephp.org/en/stable/request-options.html
20 changes: 20 additions & 0 deletions docs/tutorials/woo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# How to sync data from xxlnc search api to woo publication obejcts

## PRereq
- You have a n nexcltoud setup
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

## Prerequisites
- You have a Nextcloud setup


## Septs
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Steps


1. Mke sure you have the lates version op Open connector, open Register and Open Catalogi
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make, latest

2. make sure to remove browser cahse afther updating component (shift F5)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cache

- Create a source for the api (kan je inladen)
- test de source (for woo the default get is ok)
- OPen Register inrichten
- - Mapping aanmaken (kan je inladen maar dan meot je nog de catalugs id en publicatietype id invullen)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here we switch to dutch?

- synchronysation aanmaken (kan je inladen)

volgorde
1 - Open Register inrichten
2 - Open Catalogi inrichten
3 - Open Conencties inrichten

77 changes: 41 additions & 36 deletions lib/Controller/SynchronizationsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
use OCA\OpenConnector\Service\SynchronizationService;
use OCA\OpenConnector\Db\SynchronizationMapper;
use OCA\OpenConnector\Db\SynchronizationContractMapper;
use OCA\OpenConnector\Db\SynchronizationContractLogMapper;
use OCA\OpenConnector\Db\SynchronizationLogMapper;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\AppFramework\Http\JSONResponse;
Expand All @@ -34,7 +34,7 @@ public function __construct(
private readonly IAppConfig $config,
private readonly SynchronizationMapper $synchronizationMapper,
private readonly SynchronizationContractMapper $synchronizationContractMapper,
private readonly SynchronizationContractLogMapper $synchronizationContractLogMapper,
private readonly SynchronizationLogMapper $synchronizationLogMapper,
private readonly SynchronizationService $synchronizationService
)
{
Expand Down Expand Up @@ -203,13 +203,13 @@ public function contracts(int $id): JSONResponse
* @NoAdminRequired
* @NoCSRFRequired
*
* @param int $id The ID of the source to retrieve logs for
* @param string $uuid The UUID of the synchronization to retrieve logs for
* @return JSONResponse A JSON response containing the call logs
*/
public function logs(int $id): JSONResponse
public function logs(string $uuid): JSONResponse
{
try {
$logs = $this->synchronizationContractLogMapper->findAll(null, null, ['synchronization_id' => $id]);
$logs = $this->synchronizationLogMapper->findAll(null, null, ['synchronization_id' => $uuid]);
return new JSONResponse($logs);
} catch (DoesNotExistException $e) {
return new JSONResponse(['error' => 'Logs not found'], 404);
Expand All @@ -225,6 +225,7 @@ public function logs(int $id): JSONResponse
* @NoCSRFRequired
*
* @param int $id The ID of the synchronization
* @param bool|null $force Whether to force synchronization regardless of changes (default: false)
*
* @return JSONResponse A JSON response containing the test results
* @throws GuzzleException
Expand All @@ -233,7 +234,7 @@ public function logs(int $id): JSONResponse
*
* @example
* Request:
* empty POST
* POST with optional force parameter
*
* Response:
* {
Expand All @@ -246,7 +247,7 @@ public function logs(int $id): JSONResponse
* "validationErrors": []
* }
*/
public function test(int $id): JSONResponse
public function test(int $id, ?bool $force = false): JSONResponse
{
try {
$synchronization = $this->synchronizationMapper->find(id: $id);
Expand All @@ -257,25 +258,26 @@ public function test(int $id): JSONResponse
// Try to synchronize
try {
$logAndContractArray = $this->synchronizationService->synchronize(
synchronization: $synchronization,
isTest: true
);
synchronization: $synchronization,
isTest: true,
force: $force
);

// Return the result as a JSON response
return new JSONResponse(data: $logAndContractArray, statusCode: 200);
} catch (Exception $e) {
// Check if getHeaders method exists and use it if available
$headers = method_exists($e, 'getHeaders') ? $e->getHeaders() : [];
// Check if getHeaders method exists and use it if available
$headers = method_exists($e, 'getHeaders') ? $e->getHeaders() : [];

// If synchronization fails, return an error response
return new JSONResponse(
data: [
'error' => 'Synchronization error',
'message' => $e->getMessage()
],
statusCode: $e->getCode() ?? 400,
headers: $headers
);
data: [
'error' => 'Synchronization error',
'message' => $e->getMessage()
],
statusCode: $e->getCode() ?? 400,
headers: $headers
);
}
}

Expand All @@ -287,13 +289,15 @@ public function test(int $id): JSONResponse
*
* Endpoint: /api/synchronizations-run/{id}
*
* @param int $id The ID of the synchronization to test
* @param int $id The ID of the synchronization to run
* @param bool|null $test Whether to run in test mode (default: false)
* @param bool|null $force Whether to force synchronization regardless of changes (default: false)
* @return JSONResponse A JSON response containing the run results
* @throws GuzzleException
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws GuzzleException
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
*/
public function run(int $id): JSONResponse
public function run(int $id, ?bool $test = false, ?bool $force = false): JSONResponse
{
try {
$synchronization = $this->synchronizationMapper->find(id: $id);
Expand All @@ -304,25 +308,26 @@ public function run(int $id): JSONResponse
// Try to synchronize
try {
$logAndContractArray = $this->synchronizationService->synchronize(
synchronization: $synchronization,
isTest: false
);
synchronization: $synchronization,
isTest: $test,
force: $force
);

// Return the result as a JSON response
return new JSONResponse(data: $logAndContractArray, statusCode: 200);
} catch (Exception $e) {
// Check if getHeaders method exists and use it if available
$headers = method_exists($e, 'getHeaders') ? $e->getHeaders() : [];
// Check if getHeaders method exists and use it if available
$headers = method_exists($e, 'getHeaders') ? $e->getHeaders() : [];

// If synchronization fails, return an error response
return new JSONResponse(
data: [
'error' => 'Synchronization error',
'message' => $e->getMessage()
],
statusCode: $e->getCode() ?? 400,
headers: $headers
);
data: [
'error' => 'Synchronization error',
'message' => $e->getMessage()
],
statusCode: $e->getCode() ?? 400,
headers: $headers
);
}
}
}
6 changes: 6 additions & 0 deletions lib/Db/Mapping.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ public function __construct() {
$this->addType('dateModified', 'datetime');
}

// todo: This is a hotfix for a bug in the datamodel. Update the data model and remove this.
public function getUpdated(): DateTime
{
return $this->dateModified ?? $this->dateCreated;
}

public function getJsonFields(): array
{
return array_keys(
Expand Down
3 changes: 3 additions & 0 deletions lib/Db/SynchronizationContract.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ class SynchronizationContract extends Entity implements JsonSerializable
protected ?DateTime $targetLastChanged = null; // The last changed date of the object in the target
protected ?DateTime $targetLastChecked = null; // The last checked date of the object in the target
protected ?DateTime $targetLastSynced = null; // The last synced date of the object in the target
protected ?string $targetLastAction = null; // The last CRUD action performed on the target (create, read, update, delete)
// General
protected ?DateTime $created = null; // the date and time the synchronization was created
protected ?DateTime $updated = null; // the date and time the synchronization was updated
Expand All @@ -51,6 +52,7 @@ public function __construct() {
$this->addType('targetLastChanged', 'datetime');
$this->addType('targetLastChecked', 'datetime');
$this->addType('targetLastSynced', 'datetime');
$this->addType('targetLastAction', 'string');
$this->addType('created', 'datetime');
$this->addType('updated', 'datetime');

Expand Down Expand Up @@ -106,6 +108,7 @@ public function jsonSerialize(): array
'targetLastChanged' => isset($this->targetLastChanged) ? $this->targetLastChanged->format('c') : null,
'targetLastChecked' => isset($this->targetLastChecked) ? $this->targetLastChecked->format('c') : null,
'targetLastSynced' => isset($this->targetLastSynced) ? $this->targetLastSynced->format('c') : null,
'targetLastAction' => $this->lastAction,
'created' => isset($this->created) ? $this->created->format('c') : null,
'updated' => isset($this->updated) ? $this->updated->format('c') : null,
// @todo these 2 can be removed when migrations are merged
Expand Down
17 changes: 17 additions & 0 deletions lib/Db/SynchronizationContractLog.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,25 @@
use JsonSerializable;
use OCP\AppFramework\Db\Entity;

/**
* Class SynchronizationContractLog
*
* Entity class representing a synchronization contract log entry
*/
class SynchronizationContractLog extends Entity implements JsonSerializable
{
protected ?string $uuid = null;
protected ?string $message = null;
protected ?string $synchronizationId = null;
protected ?string $synchronizationContractId = null;
protected ?string $synchronizationLogId = null;
protected ?array $source = [];
protected ?array $target = [];
protected ?string $targetResult = null; // CRUD action taken on target (create/read/update/delete)
protected ?string $userId = null;
protected ?string $sessionId = null;
protected ?bool $test = false;
protected ?bool $force = false;
protected ?DateTime $expires = null;
protected ?DateTime $created = null;

Expand All @@ -24,10 +33,14 @@ public function __construct() {
$this->addType('message', 'string');
$this->addType('synchronizationId', 'string');
$this->addType('synchronizationContractId', 'string');
$this->addType('synchronizationLogId', 'string');
$this->addType('source', 'json');
$this->addType('target', 'json');
$this->addType('targetResult', 'string');
$this->addType('userId', 'string');
$this->addType('sessionId', 'string');
$this->addType('test', 'boolean');
$this->addType('force', 'boolean');
$this->addType('expires', 'datetime');
$this->addType('created', 'datetime');
}
Expand Down Expand Up @@ -70,10 +83,14 @@ public function jsonSerialize(): array
'message' => $this->message,
'synchronizationId' => $this->synchronizationId,
'synchronizationContractId' => $this->synchronizationContractId,
'synchronizationLogId' => $this->synchronizationLogId,
'source' => $this->source,
'target' => $this->target,
'targetResult' => $this->targetResult,
'userId' => $this->userId,
'sessionId' => $this->sessionId,
'test' => $this->test,
'force' => $this->force,
'expires' => isset($this->expires) ? $this->expires->format('c') : null,
'created' => isset($this->created) ? $this->created->format('c') : null,
];
Expand Down
33 changes: 30 additions & 3 deletions lib/Db/SynchronizationContractLogMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,22 @@
use OCP\AppFramework\Db\QBMapper;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection;
use OCP\ISession;
use OCP\IUserSession;
use Symfony\Component\Uid\Uuid;

/**
* Class SynchronizationContractLogMapper
*
* Mapper class for handling SynchronizationContractLog entities
*/
class SynchronizationContractLogMapper extends QBMapper
{
public function __construct(IDBConnection $db)
{
public function __construct(
IDBConnection $db,
private readonly IUserSession $userSession,
private readonly ISession $session
) {
parent::__construct($db, 'openconnector_synchronization_contract_logs');
}

Expand Down Expand Up @@ -79,10 +89,27 @@ public function createFromArray(array $object): SynchronizationContractLog
{
$obj = new SynchronizationContractLog();
$obj->hydrate($object);
// Set uuid

// Set uuid if not provided
if ($obj->getUuid() === null){
$obj->setUuid(Uuid::v4());
}

// Auto-fill userId from current user session
if ($obj->getUserId() === null && $this->userSession->getUser() !== null) {
$obj->setUserId($this->userSession->getUser()->getUID());
}

// Auto-fill sessionId from current session
if ($obj->getSessionId() === null) {
$obj->setSessionId($this->session->getId());
}

// If no synchronizationLogId is provided, we assume that the contract is run directly from the synchronization log and set the synchronizationLogId to n.a.
if ($obj->getSynchronizationLogId() === null) {
$obj->setSynchronizationLogId('n.a.');
}

return $this->insert($obj);
}

Expand Down
Loading
Loading