Browse Source

Merge pull request #7 from snarfed/target_domain

add new optional target_domain parameter to /webmention endpoint. for #2
pull/8/head
Aaron Parecki 9 years ago
parent
commit
1d1cb70b84
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, [ return $this->respond($response, 400, [
'error' => 'missing_parameters', '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?:\/\/[^ ]+\.[^ ]+$/'; $urlregex = '/^https?:\/\/[^ ]+\.[^ ]+$/';
$domainregex = '/^[^ ]+$/';
# Verify source, target, and callback are URLs # Verify source, target, and callback are URLs
$callback = $request->get('callback'); $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))) { ($callback && !preg_match($urlregex, $callback))) {
return $this->respond($response, 400, [ return $this->respond($response, 400, [
'error' => 'invalid_parameter', 'error' => 'invalid_parameter',
@ -90,44 +99,59 @@ class API {
$xpath = new DOMXPath($doc); $xpath = new DOMXPath($doc);
$found = false;
$found = [];
foreach($xpath->query('//a[@href]') as $href) { 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) { if(!$found) {
return $this->respond($response, 400, [ return $this->respond($response, 400, [
'error' => 'no_link_found', '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 # 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) { 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(); $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() { public function testAuthentication() {
$response = $this->webmention([]); $response = $this->webmention([]);
$this->assertEquals(401, $response->getStatusCode()); $this->assertEquals(401, $response->getStatusCode());
@ -82,6 +98,20 @@ class APITest extends PHPUnit_Framework_TestCase {
$this->assertEquals(400, $response->getStatusCode()); $this->assertEquals(400, $response->getStatusCode());
$data = json_decode($response->getContent()); $data = json_decode($response->getContent());
$this->assertEquals('missing_parameters', $data->error); $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() { public function testInvalidURLs() {
@ -122,7 +152,7 @@ class APITest extends PHPUnit_Framework_TestCase {
$this->assertEquals(false, property_exists($data, 'error')); $this->assertEquals(false, property_exists($data, 'error'));
} }
public function testQueuesWebmention() {
public function testTargetQueuesWebmention() {
$this->_createExampleAccount(); $this->_createExampleAccount();
$response = $this->webmention(['token'=>'a','source'=>'http://source.example.com/basictest','target'=>'http://target.example.com']); $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()); $data = json_decode($response->getContent());
$this->assertEquals(false, property_exists($data, 'error')); $this->assertEquals(false, property_exists($data, 'error'));
$this->assertEquals('queued', $data->status); $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() { public function testStatusNotFound() {

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

@ -9,6 +9,6 @@ Connection: keep-alive
<title>Test</title> <title>Test</title>
</head> </head>
<body class="h-entry"> <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> </body>
</html> </html>

Loading…
Cancel
Save