feat(blog): add admin endpoint for blog details and cache invalidation
- Implemented `adminDetail()` method in `BlogController` to retrieve blog posts of any status for admin editing. - Introduced `BlogCacheInvalidator` service to handle cache invalidation after blog create/update/delete actions. - Updated existing methods in `BlogController` and `RepresentationBlogController` to call cache invalidation on blog modifications. - Enhanced `BlogFormPage` and `RepresentationBlogFormPage` to utilize the new admin endpoint for fetching blog data. - Added tests for `BlogCacheInvalidator` to ensure proper functionality and error handling. - Updated documentation to reflect new API endpoint and cache invalidation behavior.
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
namespace App\Tests\Blog;
|
||||
|
||||
use App\Auth\Entity\User;
|
||||
use App\Blog\Entity\Blog;
|
||||
use App\Blog\Service\BlogCacheInvalidator;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Psr\Log\NullLogger;
|
||||
use Symfony\Component\HttpClient\MockHttpClient;
|
||||
use Symfony\Component\HttpClient\Response\MockResponse;
|
||||
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
|
||||
use Symfony\Contracts\HttpClient\HttpClientInterface;
|
||||
|
||||
/**
|
||||
* باطلسازی کش ISR سایت عمومی پس از هر نوشتن روی بلاگ.
|
||||
* fail-open است: شکست شبکه نباید ذخیرهٔ مقاله را بشکند.
|
||||
*/
|
||||
class BlogCacheInvalidatorTest extends TestCase
|
||||
{
|
||||
private function makeBlog(): Blog
|
||||
{
|
||||
return new Blog(new User('09120000000'), 'عنوان آزمایشی', 'متن آزمایشی مقاله');
|
||||
}
|
||||
|
||||
/** @param list<array{0:string,1:string,2:array}> $calls */
|
||||
private function recordingClient(array &$calls, int $status = 200): HttpClientInterface
|
||||
{
|
||||
return new MockHttpClient(function (string $method, string $url, array $options) use (&$calls, $status) {
|
||||
$calls[] = [$method, $url, $options];
|
||||
|
||||
return new MockResponse('{"revalidated":true}', ['http_code' => $status]);
|
||||
});
|
||||
}
|
||||
|
||||
public function testSendsTagsForSlugUuidAndList(): void
|
||||
{
|
||||
$calls = [];
|
||||
$blog = $this->makeBlog();
|
||||
|
||||
(new BlogCacheInvalidator(
|
||||
$this->recordingClient($calls),
|
||||
new NullLogger(),
|
||||
'https://nobat724.com/api/revalidate',
|
||||
's3cret',
|
||||
))->invalidate($blog);
|
||||
|
||||
self::assertCount(1, $calls);
|
||||
[$method, $url, $options] = $calls[0];
|
||||
self::assertSame('POST', $method);
|
||||
self::assertSame('https://nobat724.com/api/revalidate', $url);
|
||||
self::assertContains('X-Revalidate-Secret: s3cret', $options['headers']);
|
||||
|
||||
$body = json_decode($options['body'], true);
|
||||
self::assertSame(
|
||||
['blog-' . $blog->getSlug(), 'blog-' . $blog->getUuid(), 'blog-list'],
|
||||
$body['tags']
|
||||
);
|
||||
}
|
||||
|
||||
public function testNetworkFailureIsSwallowed(): void
|
||||
{
|
||||
$client = new MockHttpClient(function (): MockResponse {
|
||||
throw new class ('boom') extends \RuntimeException implements TransportExceptionInterface {};
|
||||
});
|
||||
|
||||
$invalidator = new BlogCacheInvalidator($client, new NullLogger(), 'https://nobat724.com/api/revalidate', 's3cret');
|
||||
|
||||
$invalidator->invalidate($this->makeBlog());
|
||||
self::assertTrue(true, 'شکست شبکه نباید exception بدهد');
|
||||
}
|
||||
|
||||
public function testNoRequestWhenWebhookIsNotConfigured(): void
|
||||
{
|
||||
$calls = [];
|
||||
|
||||
// URL خالی — محیط dev/تست بدون سایت عمومی
|
||||
(new BlogCacheInvalidator($this->recordingClient($calls), new NullLogger(), '', 's3cret'))
|
||||
->invalidate($this->makeBlog());
|
||||
// سکرت خالی — پیکربندی ناقص نباید درخواست بدون احراز بفرستد
|
||||
(new BlogCacheInvalidator($this->recordingClient($calls), new NullLogger(), 'https://nobat724.com/api/revalidate', ''))
|
||||
->invalidate($this->makeBlog());
|
||||
|
||||
self::assertSame([], $calls);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user