refactor: all remaining publishers use platform-specific exceptions
This commit is contained in:
parent
2320c9ae63
commit
02298f31a6
9 changed files with 19 additions and 150 deletions
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
namespace App\Services\Social;
|
||||
|
||||
use App\Exceptions\Social\BlueskyPublishException;
|
||||
use App\Exceptions\TokenExpiredException;
|
||||
use App\Models\PostPlatform;
|
||||
use App\Models\SocialAccount;
|
||||
|
|
@ -327,14 +328,6 @@ public function refreshToken(SocialAccount $account): void
|
|||
|
||||
private function handleApiError(Response $response): void
|
||||
{
|
||||
$body = $response->json() ?? [];
|
||||
$error = $body['error'] ?? 'Unknown error';
|
||||
$message = $body['message'] ?? $response->body();
|
||||
|
||||
if ($error === 'ExpiredToken' || $error === 'InvalidToken') {
|
||||
throw new TokenExpiredException("Bluesky: {$message}");
|
||||
}
|
||||
|
||||
throw new \Exception("Bluesky API error: {$message}");
|
||||
throw BlueskyPublishException::fromApiResponse($response);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
namespace App\Services\Social;
|
||||
|
||||
use App\Enums\PostPlatform\ContentType;
|
||||
use App\Exceptions\Social\LinkedInPublishException;
|
||||
use App\Exceptions\TokenExpiredException;
|
||||
use App\Models\PostPlatform;
|
||||
use App\Models\SocialAccount;
|
||||
|
|
@ -15,17 +16,6 @@
|
|||
|
||||
class LinkedInPagePublisher
|
||||
{
|
||||
/**
|
||||
* LinkedIn API error codes that indicate token issues.
|
||||
*
|
||||
* @see https://learn.microsoft.com/en-us/linkedin/shared/api-guide/concepts/error-handling
|
||||
*/
|
||||
private const TOKEN_ERROR_CODES = [
|
||||
'REVOKED_ACCESS_TOKEN',
|
||||
'EXPIRED_ACCESS_TOKEN',
|
||||
'INVALID_ACCESS_TOKEN',
|
||||
];
|
||||
|
||||
private string $baseUrl = 'https://api.linkedin.com';
|
||||
|
||||
private string $apiVersion = '202601';
|
||||
|
|
@ -492,17 +482,6 @@ private function refreshToken(SocialAccount $account): void
|
|||
|
||||
private function handleApiError(Response $response, string $context): void
|
||||
{
|
||||
$body = $response->json() ?? [];
|
||||
$errorCode = $body['code'] ?? null;
|
||||
$message = $body['message'] ?? $response->body();
|
||||
|
||||
if ($response->status() === 401 || in_array($errorCode, self::TOKEN_ERROR_CODES)) {
|
||||
throw new TokenExpiredException(
|
||||
"{$context}: {$message}",
|
||||
$errorCode
|
||||
);
|
||||
}
|
||||
|
||||
throw new \Exception("{$context}: {$message}");
|
||||
throw LinkedInPublishException::fromApiResponse($response);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
namespace App\Services\Social;
|
||||
|
||||
use App\Enums\PostPlatform\ContentType;
|
||||
use App\Exceptions\Social\LinkedInPublishException;
|
||||
use App\Exceptions\TokenExpiredException;
|
||||
use App\Models\PostPlatform;
|
||||
use App\Models\SocialAccount;
|
||||
|
|
@ -15,17 +16,6 @@
|
|||
|
||||
class LinkedInPublisher
|
||||
{
|
||||
/**
|
||||
* LinkedIn API error codes that indicate token issues.
|
||||
*
|
||||
* @see https://learn.microsoft.com/en-us/linkedin/shared/api-guide/concepts/error-handling
|
||||
*/
|
||||
private const TOKEN_ERROR_CODES = [
|
||||
'REVOKED_ACCESS_TOKEN',
|
||||
'EXPIRED_ACCESS_TOKEN',
|
||||
'INVALID_ACCESS_TOKEN',
|
||||
];
|
||||
|
||||
private string $baseUrl = 'https://api.linkedin.com';
|
||||
|
||||
private string $apiVersion = '202601';
|
||||
|
|
@ -473,17 +463,6 @@ private function refreshToken(SocialAccount $account): void
|
|||
|
||||
private function handleApiError(Response $response, string $context): void
|
||||
{
|
||||
$body = $response->json() ?? [];
|
||||
$errorCode = $body['code'] ?? null;
|
||||
$message = $body['message'] ?? $response->body();
|
||||
|
||||
if ($response->status() === 401 || in_array($errorCode, self::TOKEN_ERROR_CODES)) {
|
||||
throw new TokenExpiredException(
|
||||
"{$context}: {$message}",
|
||||
$errorCode
|
||||
);
|
||||
}
|
||||
|
||||
throw new \Exception("{$context}: {$message}");
|
||||
throw LinkedInPublishException::fromApiResponse($response);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
namespace App\Services\Social;
|
||||
|
||||
use App\Exceptions\TokenExpiredException;
|
||||
use App\Exceptions\Social\MastodonPublishException;
|
||||
use App\Models\PostPlatform;
|
||||
use App\Models\SocialAccount;
|
||||
use Illuminate\Http\Client\Response;
|
||||
|
|
@ -119,13 +119,6 @@ private function uploadMedia(SocialAccount $account, string $instance, string $u
|
|||
|
||||
private function handleApiError(Response $response): void
|
||||
{
|
||||
$body = $response->json() ?? [];
|
||||
$error = $body['error'] ?? $response->body();
|
||||
|
||||
if ($response->status() === 401 || $response->status() === 403) {
|
||||
throw new TokenExpiredException("Mastodon: {$error}");
|
||||
}
|
||||
|
||||
throw new \Exception("Mastodon API error: {$error}");
|
||||
throw MastodonPublishException::fromApiResponse($response);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
namespace App\Services\Social;
|
||||
|
||||
use App\Enums\PostPlatform\ContentType;
|
||||
use App\Exceptions\TokenExpiredException;
|
||||
use App\Exceptions\Social\PinterestPublishException;
|
||||
use App\Models\PostPlatform;
|
||||
use App\Models\SocialAccount;
|
||||
use Illuminate\Http\Client\Response;
|
||||
|
|
@ -16,14 +16,6 @@ class PinterestPublisher
|
|||
{
|
||||
private const API_BASE = 'https://api.pinterest.com/v5';
|
||||
|
||||
/**
|
||||
* Pinterest API error codes that indicate token issues.
|
||||
*/
|
||||
private const TOKEN_ERROR_CODES = [
|
||||
1, // Invalid access token
|
||||
2, // Access token has expired
|
||||
];
|
||||
|
||||
public function publish(PostPlatform $postPlatform): array
|
||||
{
|
||||
$account = $postPlatform->socialAccount;
|
||||
|
|
@ -407,20 +399,6 @@ public function getBoards(SocialAccount $account): array
|
|||
|
||||
private function handleApiError(Response $response, string $context): void
|
||||
{
|
||||
$body = $response->json() ?? [];
|
||||
$code = $body['code'] ?? $response->status();
|
||||
$message = $body['message'] ?? $response->body();
|
||||
|
||||
$isTokenError = $response->status() === 401
|
||||
|| in_array($code, self::TOKEN_ERROR_CODES);
|
||||
|
||||
if ($isTokenError) {
|
||||
throw new TokenExpiredException(
|
||||
"{$context}: {$message}",
|
||||
is_int($code) ? (string) $code : null
|
||||
);
|
||||
}
|
||||
|
||||
throw new \Exception("{$context}: {$message}");
|
||||
throw PinterestPublishException::fromApiResponse($response);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
namespace App\Services\Social;
|
||||
|
||||
use App\Exceptions\TokenExpiredException;
|
||||
use App\Exceptions\Social\ThreadsPublishException;
|
||||
use App\Models\PostPlatform;
|
||||
use App\Models\SocialAccount;
|
||||
use Illuminate\Http\Client\Response;
|
||||
|
|
@ -13,24 +13,6 @@
|
|||
|
||||
class ThreadsPublisher
|
||||
{
|
||||
/**
|
||||
* Meta Graph API error codes that indicate token issues.
|
||||
*
|
||||
* @see https://developers.facebook.com/docs/threads/error-handling
|
||||
*/
|
||||
private const TOKEN_ERROR_CODES = [
|
||||
190, // Invalid OAuth access token
|
||||
];
|
||||
|
||||
private const TOKEN_ERROR_SUBCODES = [
|
||||
458, // App not installed
|
||||
459, // User checkpointed
|
||||
460, // Password changed
|
||||
463, // Session expired
|
||||
464, // Unconfirmed user
|
||||
467, // Invalid access token
|
||||
];
|
||||
|
||||
private string $baseUrl = 'https://graph.threads.net/v1.0';
|
||||
|
||||
public function publish(PostPlatform $postPlatform): array
|
||||
|
|
@ -328,24 +310,6 @@ private function refreshToken(SocialAccount $account): void
|
|||
|
||||
private function handleApiError(Response $response, string $context): void
|
||||
{
|
||||
$body = $response->json() ?? [];
|
||||
$error = $body['error'] ?? [];
|
||||
$errorCode = $error['code'] ?? null;
|
||||
$errorSubcode = $error['error_subcode'] ?? null;
|
||||
$errorType = $error['type'] ?? null;
|
||||
$message = $error['message'] ?? $response->body();
|
||||
|
||||
$isTokenError = $errorType === 'OAuthException'
|
||||
|| in_array($errorCode, self::TOKEN_ERROR_CODES)
|
||||
|| in_array($errorSubcode, self::TOKEN_ERROR_SUBCODES);
|
||||
|
||||
if ($isTokenError) {
|
||||
throw new TokenExpiredException(
|
||||
"{$context}: {$message}",
|
||||
$errorCode ? (string) $errorCode : null
|
||||
);
|
||||
}
|
||||
|
||||
throw new \Exception("{$context}: {$message}");
|
||||
throw ThreadsPublishException::fromApiResponse($response);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
namespace App\Services\Social;
|
||||
|
||||
use App\Exceptions\Social\XPublishException;
|
||||
use App\Exceptions\TokenExpiredException;
|
||||
use App\Models\PostPlatform;
|
||||
use App\Models\SocialAccount;
|
||||
|
|
@ -14,15 +15,6 @@
|
|||
|
||||
class XPublisher
|
||||
{
|
||||
/**
|
||||
* X/Twitter API error titles that indicate token issues.
|
||||
*
|
||||
* @see https://developer.twitter.com/en/support/twitter-api/error-troubleshooting
|
||||
*/
|
||||
private const TOKEN_ERROR_TITLES = [
|
||||
'Unauthorized',
|
||||
];
|
||||
|
||||
private string $baseUrl = 'https://api.x.com';
|
||||
|
||||
private string $accessToken;
|
||||
|
|
@ -359,17 +351,6 @@ private function refreshToken(SocialAccount $account): void
|
|||
|
||||
private function handleApiError(Response $response, string $context): void
|
||||
{
|
||||
$body = $response->json() ?? [];
|
||||
$errorTitle = $body['title'] ?? null;
|
||||
$message = $body['detail'] ?? $response->body();
|
||||
|
||||
if ($response->status() === 401 || in_array($errorTitle, self::TOKEN_ERROR_TITLES)) {
|
||||
throw new TokenExpiredException(
|
||||
"{$context}: {$message}",
|
||||
$errorTitle
|
||||
);
|
||||
}
|
||||
|
||||
throw new \Exception("{$context}: {$message}");
|
||||
throw XPublishException::fromApiResponse($response);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
use App\Enums\PostPlatform\ContentType;
|
||||
use App\Enums\SocialAccount\Platform;
|
||||
use App\Exceptions\Social\MastodonPublishException;
|
||||
use App\Exceptions\TokenExpiredException;
|
||||
use App\Models\Post;
|
||||
use App\Models\PostPlatform;
|
||||
|
|
@ -158,7 +159,7 @@
|
|||
->toThrow(TokenExpiredException::class);
|
||||
});
|
||||
|
||||
test('mastodon publisher throws token expired exception on forbidden', function () {
|
||||
test('mastodon publisher throws permission exception on forbidden', function () {
|
||||
Http::fake([
|
||||
'https://mastodon.social/api/v1/statuses' => Http::response([
|
||||
'error' => 'This action is not allowed',
|
||||
|
|
@ -166,7 +167,7 @@
|
|||
]);
|
||||
|
||||
expect(fn () => $this->publisher->publish($this->postPlatform))
|
||||
->toThrow(TokenExpiredException::class);
|
||||
->toThrow(MastodonPublishException::class);
|
||||
});
|
||||
|
||||
test('mastodon publisher limits media to 4', function () {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
use App\Enums\PostPlatform\ContentType;
|
||||
use App\Enums\SocialAccount\Platform;
|
||||
use App\Exceptions\Social\XPublishException;
|
||||
use App\Exceptions\TokenExpiredException;
|
||||
use App\Models\Post;
|
||||
use App\Models\PostPlatform;
|
||||
|
|
@ -179,7 +180,7 @@
|
|||
$publisher = new XPublisher;
|
||||
|
||||
expect(fn () => $publisher->publish($this->postPlatform))
|
||||
->toThrow(Exception::class, 'X API error');
|
||||
->toThrow(XPublishException::class);
|
||||
});
|
||||
|
||||
test('x publisher handles empty error response', function () {
|
||||
|
|
|
|||
Loading…
Reference in a new issue