diff --git a/src/Endpoint.php b/src/Endpoint.php index acdcd26..dd90e10 100644 --- a/src/Endpoint.php +++ b/src/Endpoint.php @@ -16,10 +16,10 @@ use Symfony\Component\DomCrawler\Crawler; use Symfony\Contracts\HttpClient\HttpClientInterface; class Endpoint { - private readonly WebmentionGatewayInterface $gateway; function __construct( - private readonly HttpClientInterface $httpClient + private readonly HttpClientInterface $httpClient, + private readonly WebmentionGatewayInterface $gateway ) {} @@ -54,17 +54,18 @@ class Endpoint { if ($response->getStatusCode() < 400) { - $content = $this->parseContent($response->getContent(), $target); + [$type, $content] = $this->parseContent($response->getContent(), $target); $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); } else { throw new SourceNotFoundException(); } } - private function parseContent(string $content, string $target) : ?string + + private function parseContent(string $content, string $target) : array { $body = new Crawler($content); $anchors = $body->filter('a[href="'. $target . '"]'); @@ -72,8 +73,19 @@ class Endpoint { if (!$anchors->count()) { 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 diff --git a/tests/EndpointTest.php b/tests/EndpointTest.php index 52496de..5f3b487 100644 --- a/tests/EndpointTest.php +++ b/tests/EndpointTest.php @@ -5,6 +5,9 @@ use Lewisdale\Webmentions\Exceptions\InvalidTargetException; use Lewisdale\Webmentions\Exceptions\InvalidUrlException; use Lewisdale\Webmentions\Exceptions\SourceNotFoundException; 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\Attributes\TestWith; use Symfony\Contracts\HttpClient\HttpClientInterface; @@ -15,7 +18,7 @@ class EndpointTest extends TestCase { #[TestWith(["my.url.com", false])] 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"); } @@ -25,14 +28,14 @@ class EndpointTest extends TestCase { public function testThrowsInvalidUrlExceptionIfTheUrlIsInvalid(string $source, string $target) { $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); } public function testThrowsInvalidTargetExceptionIfTheTargetUrlIsNotOnCurrentSite() { $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"); } @@ -61,7 +64,7 @@ class EndpointTest extends TestCase { $this->expectException(SourceNotFoundException::class); - $endpoint = new Endpoint($mockClient); + $endpoint = new Endpoint($mockClient, $this->createMock(WebmentionGatewayInterface::class)); $endpoint->receiveWebmention($source, $target); } @@ -98,7 +101,155 @@ class EndpointTest extends TestCase { $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 = << + + + +

Some content

+

Here's some body content. It contains a url.

+

I'm liking this post.

+ + + 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 = << + + + +

Some content

+

Here's some body content. It contains a url.

+

I'm writing about this post.

+ + + 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 = << + + + +

Some content

+

Here's some body content. It contains a url.

+

I'm writing about this post.

+ + + 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); } }