Skip to content
Merged
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
4 changes: 2 additions & 2 deletions inc/Smartling/ApiWrapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ private function getAuthProvider(ConfigurationProfileEntity $profile): AuthToken
return $authProvider;
}

public function acquireLock(ConfigurationProfileEntity $profile, string $key, int $ttlSeconds): \DateTime
public function acquireLock(ConfigurationProfileEntity $profile, string $key, float $ttlSeconds): \DateTime
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

why float for seconds?

Copy link
Copy Markdown
Contributor Author

@vsolovei-smartling vsolovei-smartling May 12, 2026

Choose a reason for hiding this comment

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

Locks are short lived, but not short enough lived to be in milliseconds. The change fixes an issue no one noticed or complained about, where .001 in test lock function was silently converted to 1.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

sorry, I still do not get. lock api accepts ttl in ms. you do not need to use float to describe fraction of a second

{
return DistributedLockServiceApi::create($this->getAuthProvider($profile), $profile->getProjectId(), new NullLogger())
->acquireLock("{$profile->getProjectId()}-$key", $ttlSeconds);
Expand All @@ -125,7 +125,7 @@ public function getSourceLocale(ConfigurationProfileEntity $profile): string
return $details['sourceLocaleId'];
}

public function renewLock(ConfigurationProfileEntity $profile, string $key, int $ttlSeconds): \DateTime
public function renewLock(ConfigurationProfileEntity $profile, string $key, float $ttlSeconds): \DateTime
{
return DistributedLockServiceApi::create($this->getAuthProvider($profile), $profile->getProjectId(), $this->getLogger())
->renewLock("{$profile->getProjectId()}-$key", $ttlSeconds);
Expand Down
4 changes: 2 additions & 2 deletions inc/Smartling/ApiWrapperInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,14 @@ interface ApiWrapperInterface
/**
* @throws SmartlingApiException
*/
public function acquireLock(ConfigurationProfileEntity $profile, string $key, int $ttlSeconds): \DateTime;
public function acquireLock(ConfigurationProfileEntity $profile, string $key, float $ttlSeconds): \DateTime;

public function getSourceLocale(ConfigurationProfileEntity $profile): string;

/**
* @throws SmartlingApiException
*/
public function renewLock(ConfigurationProfileEntity $profile, string $key, int $ttlSeconds): \DateTime;
public function renewLock(ConfigurationProfileEntity $profile, string $key, float $ttlSeconds): \DateTime;

/**
* @throws SmartlingApiException
Expand Down
4 changes: 2 additions & 2 deletions inc/Smartling/ApiWrapperWithRetries.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public function __construct(private ApiWrapperInterface $base, private int $retr
{
}

public function acquireLock(ConfigurationProfileEntity $profile, string $key, int $ttlSeconds): \DateTime
public function acquireLock(ConfigurationProfileEntity $profile, string $key, float $ttlSeconds): \DateTime
{
return $this->withRetry(function () use ($profile, $key, $ttlSeconds) {
return $this->base->acquireLock($profile, $key, $ttlSeconds);
Expand All @@ -37,7 +37,7 @@ public function getSourceLocale(ConfigurationProfileEntity $profile): string
});
}

public function renewLock(ConfigurationProfileEntity $profile, string $key, int $ttlSeconds): \DateTime
public function renewLock(ConfigurationProfileEntity $profile, string $key, float $ttlSeconds): \DateTime
{
return $this->withRetry(function () use ($profile, $key, $ttlSeconds) {
return $this->base->renewLock($profile, $key, $ttlSeconds);
Expand Down
1 change: 0 additions & 1 deletion inc/Smartling/ContentTypes/Elementor/ElementAbstract.php
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,6 @@ public function setTargetContent(
}
$this->raw['elements'] = $this->elements;
foreach ($info->getOwnRelatedContent($this->id) as $path => $content) {
assert($content instanceof Content);
$this->raw = $this->setRelations($content, $externalContentElementor, $path, $submission)->toArray();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ public function getContentTypeProperty(): string

public function getEditLink(): ?string
{
return $this->wordpressFunctionProxyHelper->get_edit_post_link($this->ID);
return $this->wordpressFunctionProxyHelper->get_edit_post_link($this->ID, 'edit');
}

public function getId(): ?int
Expand Down
15 changes: 14 additions & 1 deletion inc/Smartling/WP/Table/QueueManagerTableWidget.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
use Smartling\Settings\ConfigurationProfileEntity;
use Smartling\Settings\SettingsManager;
use Smartling\Submissions\SubmissionManager;
use Smartling\Vendor\GuzzleHttp\Exception\RequestException;
use Smartling\Vendor\Smartling\Exceptions\SmartlingApiException;
use Smartling\WP\Controller\ConfigurationProfilesController;
use Smartling\WP\Controller\SmartlingListTable;
Expand Down Expand Up @@ -260,13 +261,25 @@ private function queuePurgeLink(bool $isQueueEmpty, string $queueName): string

private function getRunningMessage(SmartlingApiException $e): string
{
if (count($e->getErrorsByKey('resource.locked')) > 0) {
if ($this->isResourceLocked($e)) {
return self::MESSAGE_RUNNING;
}

return '<strong>' . __('API error (check credentials)') . ':</strong> ' . esc_html($e->getMessage());
}

private function isResourceLocked(SmartlingApiException $e): bool
{
if (count($e->getErrorsByKey('resource.locked')) > 0) {
return true;
}

$previous = $e->getPrevious();
return $previous instanceof RequestException
&& $previous->hasResponse()
&& $previous->getResponse()->getStatusCode() === 423;
}

/**
* @throws SmartlingApiException
*/
Expand Down
29 changes: 29 additions & 0 deletions tests/Smartling/WP/Table/QueueManagerTableWidgetTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ function admin_url($path = '')
use Smartling\Settings\Locale;
use Smartling\Settings\SettingsManager;
use Smartling\Submissions\SubmissionManager;
use Smartling\Vendor\GuzzleHttp\Exception\RequestException;
use Smartling\Vendor\GuzzleHttp\Psr7\Request;
use Smartling\Vendor\GuzzleHttp\Psr7\Response;
use Smartling\Vendor\Smartling\Exceptions\SmartlingApiException;
use Smartling\WP\Table\QueueManagerTableWidget;

Expand Down Expand Up @@ -149,5 +152,31 @@ public function testPrepareItemsShowsRunningMessageWhenLockHeld(): void
$uploadRow = $widget->items[0];
$this->assertStringContainsString('Running', $uploadRow['run_cron']);
}

public function testPrepareItemsShowsRunningMessageWhenSdkWrapsGuzzle423(): void
{
// Reproduces the real SDK behavior: BaseApiAbstract::sendRequest() catches
// Guzzle's ClientException for a 423 Locked response and wraps it in a
// SmartlingApiException whose errors array is empty (only the message is set).
$guzzleException = new RequestException(
'Client error: `POST https://api.smartling.com/distributed-lock-api/v2/projects/x/locks` resulted in a `423 Locked` response',
new Request('POST', 'https://api.smartling.com/distributed-lock-api/v2/projects/x/locks'),
new Response(423, [], '{"response":{"code":"RESOURCE_LOCKED","errors":[{"key":"resource.locked","message":"Failed to acquire a lock"}]}}'),
);
$api = $this->createMock(ApiWrapperInterface::class);
$api->method('acquireLock')->willThrowException(new SmartlingApiException(
'Guzzle:RequestException: ' . $guzzleException->getMessage(),
0,
$guzzleException,
));

$widget = $this->buildWidget($api, uploadQueueCount: 3);

$widget->prepare_items();

$uploadRow = $widget->items[0];
$this->assertStringContainsString('Running', $uploadRow['run_cron']);
$this->assertStringNotContainsString('API error', $uploadRow['run_cron']);
}
}
}