Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
45 changes: 45 additions & 0 deletions src/Models/ReindexCancelResponse.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php

declare(strict_types=1);

namespace BradSearch\SyncSdk\Models;

readonly class ReindexCancelResponse
{
public function __construct(
public string $status,
public string $message,
public string $taskId,
public ?string $error = null
) {}

public function isSuccess(): bool
{
return $this->status === 'success';
}

public function isError(): bool
{
return $this->status === 'error';
}

public function toArray(): array
{
return array_filter([
'status' => $this->status,
'message' => $this->message,
'task_id' => $this->taskId,
'error' => $this->error,
], fn($value) => $value !== null);
}

public static function fromArray(array $data): self
{
return new self(
status: $data['status'],
message: $data['message'],
taskId: $data['task_id'],
error: $data['error'] ?? null
);
}
}
73 changes: 73 additions & 0 deletions src/Models/ReindexStatusResponse.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<?php

declare(strict_types=1);

namespace BradSearch\SyncSdk\Models;

readonly class ReindexStatusResponse
{
public function __construct(
public string $taskId,
public string $status,
public string $message,
public ?array $progress = null,
public ?array $result = null,
public ?string $error = null,
public ?string $startedAt = null,
public ?string $updatedAt = null,
public ?string $completedAt = null,
public ?string $failedAt = null
) {}

public function isInProgress(): bool
{
return $this->status === 'in_progress';
}

public function isCompleted(): bool
{
return $this->status === 'completed';
}

public function isFailed(): bool
{
return $this->status === 'failed';
}

public function isFinished(): bool
{
return $this->isCompleted() || $this->isFailed();
}

public function toArray(): array
{
return array_filter([
'task_id' => $this->taskId,
'status' => $this->status,
'message' => $this->message,
'progress' => $this->progress,
'result' => $this->result,
'error' => $this->error,
'started_at' => $this->startedAt,
'updated_at' => $this->updatedAt,
'completed_at' => $this->completedAt,
'failed_at' => $this->failedAt,
], fn($value) => $value !== null);
}

public static function fromArray(array $data): self
{
return new self(
taskId: $data['task_id'],
status: $data['status'],
message: $data['message'],
progress: $data['progress'] ?? null,
result: $data['result'] ?? null,
error: $data['error'] ?? null,
startedAt: $data['started_at'] ?? null,
updatedAt: $data['updated_at'] ?? null,
completedAt: $data['completed_at'] ?? null,
failedAt: $data['failed_at'] ?? null
);
}
}
35 changes: 35 additions & 0 deletions src/Models/ReindexTaskResponse.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

declare(strict_types=1);

namespace BradSearch\SyncSdk\Models;

readonly class ReindexTaskResponse
{
public function __construct(
public string $status,
public string $message,
public string $taskId,
public string $statusUrl
) {}

public function toArray(): array
{
return [
'status' => $this->status,
'message' => $this->message,
'task_id' => $this->taskId,
'status_url' => $this->statusUrl,
];
}

public static function fromArray(array $data): self
{
return new self(
status: $data['status'],
message: $data['message'],
taskId: $data['task_id'],
statusUrl: $data['status_url']
);
}
}
35 changes: 32 additions & 3 deletions src/SynchronizationApiSdk.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
use BradSearch\SyncSdk\Client\HttpClient;
use BradSearch\SyncSdk\Config\SyncConfig;
use BradSearch\SyncSdk\Models\FieldConfig;
use BradSearch\SyncSdk\Models\ReindexTaskResponse;
use BradSearch\SyncSdk\Models\ReindexStatusResponse;
use BradSearch\SyncSdk\Models\ReindexCancelResponse;
use BradSearch\SyncSdk\Validators\DataValidator;
use BradSearch\SyncSdk\Exceptions\ValidationException;
use BradSearch\SyncSdk\Enums\FieldType;
Expand Down Expand Up @@ -70,13 +73,14 @@ public function createIndex(string $index): void
}

/**
* Copy source index to target index
* Copy source index to target index (async operation)
*
* @param string $sourceIndex
* @param string $targetIndex
* @return ReindexTaskResponse
* @throws ValidationException
*/
public function copyIndex(string $sourceIndex, string $targetIndex): void
public function copyIndex(string $sourceIndex, string $targetIndex): ReindexTaskResponse
{
$this->validator->validateIndex($sourceIndex);

Expand All @@ -89,7 +93,8 @@ public function copyIndex(string $sourceIndex, string $targetIndex): void
'target_index' => $targetIndex
];

$this->httpClient->post("{$this->apiStartUrl}sync/reindex", $data);
$response = $this->httpClient->post("{$this->apiStartUrl}sync/reindex", $data);
return ReindexTaskResponse::fromArray($response);
}

/**
Expand Down Expand Up @@ -291,4 +296,28 @@ private function buildEmbeddableFields(): array

return $embeddableFields;
}

/**
* Get the status of a reindex task
*
* @param string $taskId
* @return ReindexStatusResponse
*/
public function getReindexStatus(string $taskId): ReindexStatusResponse
{
$response = $this->httpClient->get("{$this->apiStartUrl}sync/reindex/status/{$taskId}");
return ReindexStatusResponse::fromArray($response);
}

/**
* Cancel a running reindex task
*
* @param string $taskId
* @return ReindexCancelResponse
*/
public function cancelReindexTask(string $taskId): ReindexCancelResponse
{
$response = $this->httpClient->delete("{$this->apiStartUrl}sync/reindex/{$taskId}");
return ReindexCancelResponse::fromArray($response);
}
}
76 changes: 66 additions & 10 deletions tests/Adapters/PrestaShopAdapterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -811,7 +811,9 @@ public function testTransformWithInvalidDescriptionTypes(): void
'remoteId' => '1807',
'sku' => 'M0E20000000EAAK',
'price' => '99.99',
'formattedPrice' => '$99.99',
'basePrice' => '9.99',
'priceTaxExcluded' => '1.00',
'basePriceTaxExcluded' => '8.44',
'localizedNames' => [
'en-US' => 'Test Product'
],
Expand Down Expand Up @@ -841,7 +843,9 @@ public function testTransformWithInvalidImageUrlTypes(): void
'remoteId' => '1807',
'sku' => 'M0E20000000EAAK',
'price' => '99.99',
'formattedPrice' => '$99.99',
'basePrice' => '9.99',
'priceTaxExcluded' => '1.00',
'basePriceTaxExcluded' => '8.44',
'localizedNames' => [
'en-US' => 'Test Product'
],
Expand All @@ -853,7 +857,9 @@ public function testTransformWithInvalidImageUrlTypes(): void
'remoteId' => '1808',
'sku' => 'M0E20000000EAAL',
'price' => '99.99',
'formattedPrice' => '$99.99',
'basePrice' => '9.99',
'priceTaxExcluded' => '1.00',
'basePriceTaxExcluded' => '8.44',
'localizedNames' => [
'en-US' => 'Test Product 2'
],
Expand Down Expand Up @@ -888,7 +894,9 @@ public function testTransformWithInvalidProductUrlTypes(): void
'remoteId' => '1807',
'sku' => 'M0E20000000EAAK',
'price' => '99.99',
'formattedPrice' => '$99.99',
'basePrice' => '9.99',
'priceTaxExcluded' => '1.00',
'basePriceTaxExcluded' => '8.44',
'localizedNames' => [
'en-US' => 'Test Product'
],
Expand All @@ -900,7 +908,9 @@ public function testTransformWithInvalidProductUrlTypes(): void
'remoteId' => '1808',
'sku' => 'M0E20000000EAAL',
'price' => '99.99',
'formattedPrice' => '$99.99',
'basePrice' => '9.99',
'priceTaxExcluded' => '1.00',
'basePriceTaxExcluded' => '8.44',
'localizedNames' => [
'en-US' => 'Test Product 2'
],
Expand Down Expand Up @@ -936,7 +946,9 @@ public function testTransformWithInvalidCategoriesTypes(): void
'remoteId' => '1807',
'sku' => 'M0E20000000EAAK',
'price' => '99.99',
'formattedPrice' => '$99.99',
'basePrice' => '9.99',
'priceTaxExcluded' => '1.00',
'basePriceTaxExcluded' => '8.44',
'localizedNames' => [
'en-US' => 'Test Product'
],
Expand All @@ -948,7 +960,9 @@ public function testTransformWithInvalidCategoriesTypes(): void
'remoteId' => '1808',
'sku' => 'M0E20000000EAAL',
'price' => '99.99',
'formattedPrice' => '$99.99',
'basePrice' => '9.99',
'priceTaxExcluded' => '1.00',
'basePriceTaxExcluded' => '8.44',
'localizedNames' => [
'en-US' => 'Test Product 2'
],
Expand Down Expand Up @@ -1013,7 +1027,9 @@ public function testTransformWithInvalidLocalizedValues(): void
'remoteId' => '1807',
'sku' => 'M0E20000000EAAK',
'price' => '99.99',
'formattedPrice' => '$99.99',
'basePrice' => '9.99',
'priceTaxExcluded' => '1.00',
'basePriceTaxExcluded' => '8.44',
'localizedNames' => [
'en-US' => null, // Null name
'lt-LT' => '', // Empty name
Expand Down Expand Up @@ -1074,7 +1090,9 @@ public function testTransformWithInvalidFeaturesTypes(): void
'remoteId' => '1807',
'sku' => 'M0E20000000EAAK',
'price' => '99.99',
'formattedPrice' => '$99.99',
'basePrice' => '9.99',
'priceTaxExcluded' => '1.00',
'basePriceTaxExcluded' => '8.44',
'localizedNames' => [
'en-US' => 'Test Product'
],
Expand Down Expand Up @@ -1129,7 +1147,9 @@ public function testTransformWithInvalidVariantsTypes(): void
'remoteId' => '1807',
'sku' => 'M0E20000000EAAK',
'price' => '99.99',
'formattedPrice' => '$99.99',
'basePrice' => '9.99',
'priceTaxExcluded' => '1.00',
'basePriceTaxExcluded' => '8.44',
'localizedNames' => [
'en-US' => 'Test Product'
],
Expand Down Expand Up @@ -1159,4 +1179,40 @@ public function testTransformWithInvalidVariantsTypes(): void
$this->assertCount(1, $result["products"]);
$this->assertEmpty($this->getProductFromResult($result)['variants']); // All variants invalid or filtered out
}

/**
* Test that zero price values are handled correctly and not skipped
*/
public function testTransformWithZeroPriceValues(): void
{
$prestaShopData = [
'products' => [
[
'remoteId' => '1807',
'sku' => 'FREE-PRODUCT',
'price' => '0', // Zero price as string
'basePrice' => 0, // Zero price as number
'priceTaxExcluded' => '0.00', // Zero price with decimal
'basePriceTaxExcluded' => 0.0, // Zero price as float
'localizedNames' => [
'en-US' => 'Free Product'
],
'categories' => [],
'variants' => []
]
]
];

$result = $this->adapter->transform($prestaShopData);

$this->assertCount(1, $result['products']);
$product = $this->getProductFromResult($result);

// Verify that all zero price values are correctly handled
$this->assertEquals('0', $product['price']);
$this->assertEquals('0', $product['basePrice']);
$this->assertEquals('0.00', $product['priceTaxExcluded']);
$this->assertEquals('0', $product['basePriceTaxExcluded']);
$this->assertEquals('Free Product', $product['name']);
}
}
Loading