Browse Source

add new optional target_domain parameter to /webmention endpoint. for #2

when used instead of target, finds all links to the target domain and subdomains, enqueues webmentions for them all, and returns all status URLs in the response's 'location' field. the HTTP Location header is omitted.
pull/7/head
Ryan Barrett 9 years ago
parent
commit
82b26d15fe
3 changed files with 112 additions and 44 deletions
  1. +54
    -30
      controllers/API.php
  2. +57
    -13
      tests/APITest.php
  3. +1
    -1
      tests/data/source.example.com/basictest

+ 54
- 30
controllers/API.php View File

@ -44,19 +44,28 @@ class API {
]);
}
# Require source and target parameters
if((!$source=$request->get('source')) || (!$target=$request->get('target'))) {
# Require source and target or target_domain parameters
$target = $target_domain = null;
if((!$source=$request->get('source')) || ((!$target=$request->get('target')) && (!$target_domain=$request->get('target_domain')))) {
return $this->respond($response, 400, [
'error' => 'missing_parameters',
'error_description' => 'The source or target parameters were missing'
'error_description' => 'The source or target or target_domain parameters were missing'
]);
}
if($target && $target_domain) {
return $this->respond($response, 400, [
'error' => 'invalid_parameter',
'error_description' => 'Can\'t provide both target and target_domain together'
]);
}
$urlregex = '/^https?:\/\/[^ ]+\.[^ ]+$/';
$domainregex = '/^[^ ]+$/';
# Verify source, target, and callback are URLs
$callback = $request->get('callback');
if(!preg_match($urlregex, $source) || !preg_match($urlregex, $target) ||
if(!preg_match($urlregex, $source) ||
(!preg_match($urlregex, $target) && !preg_match($domainregex, $target_domain)) ||
($callback && !preg_match($urlregex, $callback))) {
return $this->respond($response, 400, [
'error' => 'invalid_parameter',
@ -90,44 +99,59 @@ class API {
$xpath = new DOMXPath($doc);
$found = false;
$found = [];
foreach($xpath->query('//a[@href]') as $href) {
if($href->getAttribute('href') == $target) {
$found = true;
continue;
$url = $href->getAttribute('href');
$domain = parse_url($url, PHP_URL_HOST);
if($url == $target || $domain == $target_domain ||
# subdomain check
($target_domain and substr_compare($domain, '.' . $target_domain, -(strlen($target_domain) + 1)) == 0)) {
$found[$url] = null;
}
}
if(!$found) {
return $this->respond($response, 400, [
'error' => 'no_link_found',
'error_description' => 'The source document does not have a link to the target URL'
'error_description' => 'The source document does not have a link to the target URL or domain'
]);
}
# Everything checked out, so write the webmention to the log and queue a job to start sending
# TODO: database transaction?
$statusURLs = [];
foreach($found as $url=>$_) {
$w = ORM::for_table('webmentions')->create();
$w->site_id = $role->site_id;
$w->created_by = $role->user_id;
$w->created_at = date('Y-m-d H:i:s');
$w->token = self::generateStatusToken();
$w->source = $source;
$w->target = $url;
$w->vouch = $request->get('vouch');
$w->callback = $callback;
$w->save();
q()->queue('Telegraph\Webmention', 'send', [$w->id]);
$statusURLs[] = Config::$base . 'webmention/' . $w->token;
}
$w = ORM::for_table('webmentions')->create();
$w->site_id = $role->site_id;
$w->created_by = $role->user_id;
$w->created_at = date('Y-m-d H:i:s');
$w->token = self::generateStatusToken();
$w->source = $source;
$w->target = $target;
$w->vouch = $request->get('vouch');
$w->callback = $callback;
$w->save();
q()->queue('Telegraph\Webmention', 'send', [$w->id]);
$statusURL = Config::$base . 'webmention/' . $w->token;
return $this->respond($response, 201, [
'status' => 'queued',
'location' => $statusURL
], [
'Location' => $statusURL
]);
if ($target) {
$body = [
'status' => 'queued',
'location' => $statusURLs[0]
];
$headers = ['Location' => $statusURLs[0]];
} else {
$body = [
'status' => 'queued',
'location' => $statusURLs
];
$headers = [];
}
return $this->respond($response, 201, $body, $headers);
}
public function webmention_status(Request $request, Response $response, $args) {

+ 57
- 13
tests/APITest.php View File

@ -46,6 +46,22 @@ class APITest extends PHPUnit_Framework_TestCase {
$role->save();
}
private function _assertQueued($source, $target, $status_url) {
preg_match('/\/webmention\/(.+)/', $status_url, $match);
$this->assertNotNull($match);
# Verify it queued the mention in the database
$d = ORM::for_table('webmentions')->where(['source' => $source, 'target' => $target])->find_one();
$this->assertNotNull($d);
$this->assertEquals($match[1], $d->token);
# Check the status endpoint to make sure it says it's still queued
$response = $this->status($d->token);
$this->assertEquals(200, $response->getStatusCode());
$data = json_decode($response->getContent());
$this->assertEquals('queued', $data->status);
}
public function testAuthentication() {
$response = $this->webmention([]);
$this->assertEquals(401, $response->getStatusCode());
@ -82,6 +98,20 @@ class APITest extends PHPUnit_Framework_TestCase {
$this->assertEquals(400, $response->getStatusCode());
$data = json_decode($response->getContent());
$this->assertEquals('missing_parameters', $data->error);
$response = $this->webmention(['token'=>'a','target_domain'=>'foo']);
$this->assertEquals(400, $response->getStatusCode());
$data = json_decode($response->getContent());
$this->assertEquals('missing_parameters', $data->error);
}
public function testTargetAndTargetDomain() {
$this->_createExampleAccount();
$response = $this->webmention(['token'=>'a','source'=>'foo','target'=>'foo','target_domain'=>'foo']);
$this->assertEquals(400, $response->getStatusCode());
$data = json_decode($response->getContent());
$this->assertEquals('invalid_parameter', $data->error);
}
public function testInvalidURLs() {
@ -122,7 +152,7 @@ class APITest extends PHPUnit_Framework_TestCase {
$this->assertEquals(false, property_exists($data, 'error'));
}
public function testQueuesWebmention() {
public function testTargetQueuesWebmention() {
$this->_createExampleAccount();
$response = $this->webmention(['token'=>'a','source'=>'http://source.example.com/basictest','target'=>'http://target.example.com']);
@ -130,21 +160,35 @@ class APITest extends PHPUnit_Framework_TestCase {
$data = json_decode($response->getContent());
$this->assertEquals(false, property_exists($data, 'error'));
$this->assertEquals('queued', $data->status);
$this->assertEquals(true, property_exists($data, 'location'));
$this->_assertQueued('http://source.example.com/basictest', 'http://target.example.com', $data->location);
}
preg_match('/\/webmention\/(.+)/', $data->location, $match);
$this->assertNotNull($match);
public function testTargetDomainQueuesOneWebmention() {
$this->_createExampleAccount();
# Verify it queued the mention in the database
$d = ORM::for_table('webmentions')->where(['source' => 'http://source.example.com/basictest', 'target' => 'http://target.example.com'])->find_one();
$this->assertNotNull($d);
$this->assertEquals($match[1], $d->token);
$response = $this->webmention(['token'=>'a','source'=>'http://source.example.com/basictest','target_domain'=>'target.example.com']);
$body = $response->getContent();
$this->assertEquals(201, $response->getStatusCode(), $body);
$data = json_decode($body);
$this->assertEquals(false, property_exists($data, 'error'), $body);
$this->assertEquals('queued', $data->status, $body);
$this->assertEquals(true, property_exists($data, 'location'), $body);
$this->assertEquals(1, count($data->location), $body);
$this->_assertQueued('http://source.example.com/basictest', 'http://target.example.com', $data->location[0]);
}
# Check the status endpoint to make sure it says it's still queued
$response = $this->status($d->token);
$this->assertEquals(200, $response->getStatusCode());
$data = json_decode($response->getContent());
$this->assertEquals('queued', $data->status);
public function testTargetDomainQueuesMultipleWebmentions() {
$this->_createExampleAccount();
$response = $this->webmention(['token'=>'a','source'=>'http://source.example.com/basictest','target_domain'=>'example.com']);
$body = $response->getContent();
$this->assertEquals(201, $response->getStatusCode(), $body);
$data = json_decode($body);
$this->assertEquals(false, property_exists($data, 'error'), $body);
$this->assertEquals('queued', $data->status, $body);
$this->assertEquals(2, count($data->location), $body);
$this->_assertQueued('http://source.example.com/basictest', 'http://target.example.com', $data->location[0]);
$this->_assertQueued('http://source.example.com/basictest', 'http://target2.example.com', $data->location[1]);
}
public function testStatusNotFound() {

+ 1
- 1
tests/data/source.example.com/basictest View File

@ -9,6 +9,6 @@ Connection: keep-alive
<title>Test</title>
</head>
<body class="h-entry">
<p class="e-content">This page has a link to <a href="http://target.example.com">target.example.com</a>.</p>
<p class="e-content">This page has links to <a href="http://target.example.com">target.example.com</a> and <a href="http://target2.example.com">target2.example.com</a>.</p>
</body>
</html>

Loading…
Cancel
Save