Parse likes and reply types using microformats
This commit is contained in:
parent
9d9e465a37
commit
0c316d18a2
@ -16,10 +16,10 @@ use Symfony\Component\DomCrawler\Crawler;
|
|||||||
use Symfony\Contracts\HttpClient\HttpClientInterface;
|
use Symfony\Contracts\HttpClient\HttpClientInterface;
|
||||||
|
|
||||||
class Endpoint {
|
class Endpoint {
|
||||||
private readonly WebmentionGatewayInterface $gateway;
|
|
||||||
|
|
||||||
function __construct(
|
function __construct(
|
||||||
private readonly HttpClientInterface $httpClient
|
private readonly HttpClientInterface $httpClient,
|
||||||
|
private readonly WebmentionGatewayInterface $gateway
|
||||||
)
|
)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
@ -54,17 +54,18 @@ class Endpoint {
|
|||||||
|
|
||||||
if ($response->getStatusCode() < 400)
|
if ($response->getStatusCode() < 400)
|
||||||
{
|
{
|
||||||
$content = $this->parseContent($response->getContent(), $target);
|
[$type, $content] = $this->parseContent($response->getContent(), $target);
|
||||||
$author = $this->parseAuthor($response->getContent());
|
$author = $this->parseAuthor($response->getContent());
|
||||||
|
|
||||||
$webmention = new Webmention(null, $target, $source, MentionType::from(""), $content, $author);
|
$webmention = new Webmention(null, $target, $source, $type, $content, $author);
|
||||||
$this->gateway->save($webmention);
|
$this->gateway->save($webmention);
|
||||||
} else {
|
} else {
|
||||||
throw new SourceNotFoundException();
|
throw new SourceNotFoundException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function parseContent(string $content, string $target) : ?string
|
|
||||||
|
private function parseContent(string $content, string $target) : array
|
||||||
{
|
{
|
||||||
$body = new Crawler($content);
|
$body = new Crawler($content);
|
||||||
$anchors = $body->filter('a[href="'. $target . '"]');
|
$anchors = $body->filter('a[href="'. $target . '"]');
|
||||||
@ -72,8 +73,19 @@ class Endpoint {
|
|||||||
if (!$anchors->count()) {
|
if (!$anchors->count()) {
|
||||||
throw new TargetNotMentionedException();
|
throw new TargetNotMentionedException();
|
||||||
}
|
}
|
||||||
|
$type = $this->classToMentionType($anchors->attr('class'));
|
||||||
|
return [$type, null];
|
||||||
|
}
|
||||||
|
|
||||||
return null;
|
private function classToMentionType(string $class = "") : MentionType
|
||||||
|
{
|
||||||
|
if (str_contains($class, "u-like-of")) {
|
||||||
|
return MentionType::Like;
|
||||||
|
} else if (str_contains($class, "u-reply-to")) {
|
||||||
|
return MentionType::Reply;
|
||||||
|
}
|
||||||
|
|
||||||
|
return MentionType::Mention;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function parseAuthor(string $author) : ?string
|
private function parseAuthor(string $author) : ?string
|
||||||
|
@ -5,6 +5,9 @@ use Lewisdale\Webmentions\Exceptions\InvalidTargetException;
|
|||||||
use Lewisdale\Webmentions\Exceptions\InvalidUrlException;
|
use Lewisdale\Webmentions\Exceptions\InvalidUrlException;
|
||||||
use Lewisdale\Webmentions\Exceptions\SourceNotFoundException;
|
use Lewisdale\Webmentions\Exceptions\SourceNotFoundException;
|
||||||
use Lewisdale\Webmentions\Exceptions\TargetNotMentionedException;
|
use Lewisdale\Webmentions\Exceptions\TargetNotMentionedException;
|
||||||
|
use Lewisdale\Webmentions\Gateways\WebmentionGatewayInterface;
|
||||||
|
use Lewisdale\Webmentions\Models\MentionType;
|
||||||
|
use Lewisdale\Webmentions\Models\Webmention;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use PHPUnit\Framework\Attributes\TestWith;
|
use PHPUnit\Framework\Attributes\TestWith;
|
||||||
use Symfony\Contracts\HttpClient\HttpClientInterface;
|
use Symfony\Contracts\HttpClient\HttpClientInterface;
|
||||||
@ -15,7 +18,7 @@ class EndpointTest extends TestCase {
|
|||||||
#[TestWith(["my.url.com", false])]
|
#[TestWith(["my.url.com", false])]
|
||||||
public function testValidatesUrls(string $url, bool $expected)
|
public function testValidatesUrls(string $url, bool $expected)
|
||||||
{
|
{
|
||||||
$endpoint = new Endpoint($this->createMock(HttpClientInterface::class));
|
$endpoint = new Endpoint($this->createMock(HttpClientInterface::class), $this->createMock(WebmentionGatewayInterface::class));
|
||||||
$this->assertEquals($expected, $endpoint->validateUrl($url), "Expected $url");
|
$this->assertEquals($expected, $endpoint->validateUrl($url), "Expected $url");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -25,14 +28,14 @@ class EndpointTest extends TestCase {
|
|||||||
public function testThrowsInvalidUrlExceptionIfTheUrlIsInvalid(string $source, string $target)
|
public function testThrowsInvalidUrlExceptionIfTheUrlIsInvalid(string $source, string $target)
|
||||||
{
|
{
|
||||||
$this->expectException(InvalidUrlException::class);
|
$this->expectException(InvalidUrlException::class);
|
||||||
$endpoint = new Endpoint($this->createMock(HttpClientInterface::class));
|
$endpoint = new Endpoint($this->createMock(HttpClientInterface::class), $this->createMock(WebmentionGatewayInterface::class));
|
||||||
$endpoint->receiveWebmention($source, $target);
|
$endpoint->receiveWebmention($source, $target);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testThrowsInvalidTargetExceptionIfTheTargetUrlIsNotOnCurrentSite()
|
public function testThrowsInvalidTargetExceptionIfTheTargetUrlIsNotOnCurrentSite()
|
||||||
{
|
{
|
||||||
$this->expectException(InvalidTargetException::class);
|
$this->expectException(InvalidTargetException::class);
|
||||||
$endpoint = new Endpoint($this->createMock(HttpClientInterface::class));
|
$endpoint = new Endpoint($this->createMock(HttpClientInterface::class), $this->createMock(WebmentionGatewayInterface::class));
|
||||||
$endpoint->receiveWebmention("https://a-valid-source.url", "https://not-my-site.com");
|
$endpoint->receiveWebmention("https://a-valid-source.url", "https://not-my-site.com");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,7 +64,7 @@ class EndpointTest extends TestCase {
|
|||||||
|
|
||||||
$this->expectException(SourceNotFoundException::class);
|
$this->expectException(SourceNotFoundException::class);
|
||||||
|
|
||||||
$endpoint = new Endpoint($mockClient);
|
$endpoint = new Endpoint($mockClient, $this->createMock(WebmentionGatewayInterface::class));
|
||||||
$endpoint->receiveWebmention($source, $target);
|
$endpoint->receiveWebmention($source, $target);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,7 +101,155 @@ class EndpointTest extends TestCase {
|
|||||||
|
|
||||||
$this->expectException(TargetNotMentionedException::class);
|
$this->expectException(TargetNotMentionedException::class);
|
||||||
|
|
||||||
$endpoint = new Endpoint($mockClient);
|
$endpoint = new Endpoint($mockClient, $this->createMock(WebmentionGatewayInterface::class));
|
||||||
|
$endpoint->receiveWebmention($source, $target);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testItShouldParseAWebmentionAsALikeIfItHasTheCorrectMicroFormat()
|
||||||
|
{
|
||||||
|
$source = "https://my-valid-source-url.com";
|
||||||
|
$target = "https://lewisdale.dev/post/a-post-page";
|
||||||
|
|
||||||
|
$content = <<<XML
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Some content</h1>
|
||||||
|
<p>Here's some body content. It <a href="/another/page">contains a url</a>.</p>
|
||||||
|
<p>I'm liking <a href="$target" class="u-like-of">this post</a>.</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
XML;
|
||||||
|
|
||||||
|
$mockClient = $this->createMock(HttpClientInterface::class);
|
||||||
|
$mockResponse = $this->createMock(ResponseInterface::class);
|
||||||
|
$mockGateway = $this->createMock(WebmentionGatewayInterface::class);
|
||||||
|
|
||||||
|
$mockClient->expects($this->once())
|
||||||
|
->method('request')
|
||||||
|
->with($this->identicalTo('GET'), $this->identicalTo($source))
|
||||||
|
->will($this->returnValue($mockResponse));
|
||||||
|
|
||||||
|
$mockResponse->method('getStatusCode')
|
||||||
|
->will($this->returnValue(200));
|
||||||
|
|
||||||
|
$mockResponse->method('getContent')
|
||||||
|
->willReturn($content);
|
||||||
|
|
||||||
|
$mockGateway->expects($this->once())
|
||||||
|
->method('save')
|
||||||
|
->with($this->equalTo(
|
||||||
|
new Webmention(
|
||||||
|
null,
|
||||||
|
$target,
|
||||||
|
$source,
|
||||||
|
MentionType::Like,
|
||||||
|
null,
|
||||||
|
null
|
||||||
|
))
|
||||||
|
);
|
||||||
|
|
||||||
|
$endpoint = new Endpoint($mockClient, $mockGateway);
|
||||||
|
|
||||||
|
$endpoint->receiveWebmention($source, $target);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testItShouldParseAWebmentionAsAMentionIfItHasNoMicroformat()
|
||||||
|
{
|
||||||
|
$source = "https://my-valid-source-url.com";
|
||||||
|
$target = "https://lewisdale.dev/post/a-post-page";
|
||||||
|
|
||||||
|
$content = <<<XML
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Some content</h1>
|
||||||
|
<p>Here's some body content. It <a href="/another/page">contains a url</a>.</p>
|
||||||
|
<p>I'm writing about <a href="$target" class="my-link">this post</a>.</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
XML;
|
||||||
|
|
||||||
|
$mockClient = $this->createMock(HttpClientInterface::class);
|
||||||
|
$mockResponse = $this->createMock(ResponseInterface::class);
|
||||||
|
$mockGateway = $this->createMock(WebmentionGatewayInterface::class);
|
||||||
|
|
||||||
|
$mockClient->expects($this->once())
|
||||||
|
->method('request')
|
||||||
|
->with($this->identicalTo('GET'), $this->identicalTo($source))
|
||||||
|
->will($this->returnValue($mockResponse));
|
||||||
|
|
||||||
|
$mockResponse->method('getStatusCode')
|
||||||
|
->will($this->returnValue(200));
|
||||||
|
|
||||||
|
$mockResponse->method('getContent')
|
||||||
|
->willReturn($content);
|
||||||
|
|
||||||
|
$mockGateway->expects($this->once())
|
||||||
|
->method('save')
|
||||||
|
->with($this->equalTo(
|
||||||
|
new Webmention(
|
||||||
|
null,
|
||||||
|
$target,
|
||||||
|
$source,
|
||||||
|
MentionType::Mention,
|
||||||
|
null,
|
||||||
|
null
|
||||||
|
))
|
||||||
|
);
|
||||||
|
|
||||||
|
$endpoint = new Endpoint($mockClient, $mockGateway);
|
||||||
|
$endpoint->receiveWebmention($source, $target);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testItShouldParseAWebmentionAsAReplyIfItHasTheCorrectMicroFormat()
|
||||||
|
{
|
||||||
|
$source = "https://my-valid-source-url.com";
|
||||||
|
$target = "https://lewisdale.dev/post/a-post-page";
|
||||||
|
|
||||||
|
$content = <<<XML
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Some content</h1>
|
||||||
|
<p>Here's some body content. It <a href="/another/page">contains a url</a>.</p>
|
||||||
|
<p>I'm writing about <a href="$target" class="u-reply-to">this post</a>.</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
XML;
|
||||||
|
|
||||||
|
$mockClient = $this->createMock(HttpClientInterface::class);
|
||||||
|
$mockResponse = $this->createMock(ResponseInterface::class);
|
||||||
|
$mockGateway = $this->createMock(WebmentionGatewayInterface::class);
|
||||||
|
|
||||||
|
$mockClient->expects($this->once())
|
||||||
|
->method('request')
|
||||||
|
->with($this->identicalTo('GET'), $this->identicalTo($source))
|
||||||
|
->will($this->returnValue($mockResponse));
|
||||||
|
|
||||||
|
$mockResponse->method('getStatusCode')
|
||||||
|
->will($this->returnValue(200));
|
||||||
|
|
||||||
|
$mockResponse->method('getContent')
|
||||||
|
->willReturn($content);
|
||||||
|
|
||||||
|
$mockGateway->expects($this->once())
|
||||||
|
->method('save')
|
||||||
|
->with($this->equalTo(
|
||||||
|
new Webmention(
|
||||||
|
null,
|
||||||
|
$target,
|
||||||
|
$source,
|
||||||
|
MentionType::Reply,
|
||||||
|
null,
|
||||||
|
null
|
||||||
|
))
|
||||||
|
);
|
||||||
|
|
||||||
|
$endpoint = new Endpoint($mockClient, $mockGateway);
|
||||||
$endpoint->receiveWebmention($source, $target);
|
$endpoint->receiveWebmention($source, $target);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user