MTM Micropub improvements and WebMention fix #79
6 changed files with 93 additions and 95 deletions
Implement micropub update support with clean exception-based error handling
- Add MicropubUnsupportedModelException for non-note update attempts - Refactor UpdateHandler to throw exceptions instead of returning JsonResponse objects; extract applySyndication() to DRY up duplicated syndication URL mapping - Fix MicropubController: InvalidTokenScopeException now returns 401 + insufficient_scope; add catches for ModelNotFoundException (404) and MicropubUnsupportedModelException (500); updates return 200 not 201 - Slim MicropubRequest.normalizeMicropubJson() to branch on action type, keeping update and create fields cleanly separated - Update tests to match corrected status codes and error keys; remove markTestSkipped() from all update tests Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
commit
24cb6d61aa
7
app/Exceptions/MicropubUnsupportedModelException.php
Normal file
7
app/Exceptions/MicropubUnsupportedModelException.php
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Exceptions;
|
||||||
|
|
||||||
|
class MicropubUnsupportedModelException extends \Exception {}
|
||||||
|
|
@ -6,10 +6,12 @@ namespace App\Http\Controllers;
|
||||||
|
|
||||||
use App\Exceptions\InvalidTokenScopeException;
|
use App\Exceptions\InvalidTokenScopeException;
|
||||||
use App\Exceptions\MicropubHandlerException;
|
use App\Exceptions\MicropubHandlerException;
|
||||||
|
use App\Exceptions\MicropubUnsupportedModelException;
|
||||||
use App\Http\Requests\MicropubRequest;
|
use App\Http\Requests\MicropubRequest;
|
||||||
use App\Models\Place;
|
use App\Models\Place;
|
||||||
use App\Models\SyndicationTarget;
|
use App\Models\SyndicationTarget;
|
||||||
use App\Services\Micropub\MicropubHandlerRegistry;
|
use App\Services\Micropub\MicropubHandlerRegistry;
|
||||||
|
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||||
use Illuminate\Http\JsonResponse;
|
use Illuminate\Http\JsonResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Lcobucci\JWT\Token;
|
use Lcobucci\JWT\Token;
|
||||||
|
|
@ -45,11 +47,31 @@ class MicropubController extends Controller
|
||||||
$handler = $this->handlerRegistry->getHandler($type);
|
$handler = $this->handlerRegistry->getHandler($type);
|
||||||
$result = $handler->handle($request->getMicropubData());
|
$result = $handler->handle($request->getMicropubData());
|
||||||
|
|
||||||
// Return appropriate response based on the handler result
|
if ($result['response'] === 'updated') {
|
||||||
|
return response()->json([
|
||||||
|
'response' => $result['response'],
|
||||||
|
], 200)->header('Location', $result['url']);
|
||||||
|
}
|
||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'response' => $result['response'],
|
'response' => $result['response'],
|
||||||
'location' => $result['url'] ?? null,
|
'location' => $result['url'] ?? null,
|
||||||
], 201)->header('Location', $result['url']);
|
], 201)->header('Location', $result['url']);
|
||||||
|
} catch (InvalidTokenScopeException) {
|
||||||
|
return response()->json([
|
||||||
|
'error' => 'insufficient_scope',
|
||||||
|
'error_description' => 'The token does not have the required scope for this request',
|
||||||
|
], 401);
|
||||||
|
} catch (ModelNotFoundException) {
|
||||||
|
return response()->json([
|
||||||
|
'error' => 'invalid_request',
|
||||||
|
'error_description' => 'No known note with given ID',
|
||||||
|
], 404);
|
||||||
|
} catch (MicropubUnsupportedModelException) {
|
||||||
|
return response()->json([
|
||||||
|
'error' => 'invalid',
|
||||||
|
'error_description' => 'This implementation currently only supports the updating of notes',
|
||||||
|
], 500);
|
||||||
} catch (\InvalidArgumentException $e) {
|
} catch (\InvalidArgumentException $e) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'error' => 'invalid_request',
|
'error' => 'invalid_request',
|
||||||
|
|
@ -57,15 +79,10 @@ class MicropubController extends Controller
|
||||||
], 400);
|
], 400);
|
||||||
} catch (MicropubHandlerException) {
|
} catch (MicropubHandlerException) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'error' => 'Unknown Micropub type',
|
'error' => 'unsupported_operation',
|
||||||
'error_description' => 'The request could not be processed by this server',
|
'error_description' => 'The request could not be processed by this server',
|
||||||
], 500);
|
], 500);
|
||||||
} catch (InvalidTokenScopeException) {
|
} catch (\Exception $e) {
|
||||||
return response()->json([
|
|
||||||
'error' => 'invalid_scope',
|
|
||||||
'error_description' => 'The token does not have the required scope for this request',
|
|
||||||
], 403);
|
|
||||||
} catch (\Exception) {
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'error' => 'server_error',
|
'error' => 'server_error',
|
||||||
'error_description' => 'An error occurred processing the request',
|
'error_description' => 'An error occurred processing the request',
|
||||||
|
|
|
||||||
|
|
@ -48,20 +48,25 @@ class MicropubRequest extends FormRequest
|
||||||
|
|
||||||
$data = $json->all();
|
$data = $json->all();
|
||||||
|
|
||||||
// Convert JSON type (h-entry) to simple type (entry)
|
$this->micropubData['token_data'] = $data['token_data'];
|
||||||
|
|
||||||
|
if (isset($data['action']) && $data['action'] === 'update') {
|
||||||
|
$this->micropubData['type'] = 'update';
|
||||||
|
$this->micropubData['update_url'] = $data['url'] ?? null;
|
||||||
|
$this->micropubData['update_replace'] = $data['replace'] ?? null;
|
||||||
|
$this->micropubData['update_add'] = $data['add'] ?? null;
|
||||||
|
$this->micropubData['update_delete'] = $data['delete'] ?? null;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create request — normalize h-type and properties
|
||||||
if (isset($data['type']) && is_array($data['type'])) {
|
if (isset($data['type']) && is_array($data['type'])) {
|
||||||
$type = current($data['type']);
|
$type = current($data['type']);
|
||||||
if (str_starts_with($type, 'h-')) {
|
if (str_starts_with($type, 'h-')) {
|
||||||
$this->micropubData['type'] = substr($type, 2);
|
$this->micropubData['type'] = substr($type, 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Or set the type to update
|
|
||||||
elseif (isset($data['action']) && $data['action'] === 'update') {
|
|
||||||
$this->micropubData['type'] = 'update';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add in the token data
|
|
||||||
$this->micropubData['token_data'] = $data['token_data'];
|
|
||||||
|
|
||||||
// Add h-entry values
|
// Add h-entry values
|
||||||
$this->micropubData['content'] = Arr::get($data, 'properties.content.0');
|
$this->micropubData['content'] = Arr::get($data, 'properties.content.0');
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ namespace App\Providers;
|
||||||
use App\Services\Micropub\CardHandler;
|
use App\Services\Micropub\CardHandler;
|
||||||
use App\Services\Micropub\EntryHandler;
|
use App\Services\Micropub\EntryHandler;
|
||||||
use App\Services\Micropub\MicropubHandlerRegistry;
|
use App\Services\Micropub\MicropubHandlerRegistry;
|
||||||
|
use App\Services\Micropub\UpdateHandler;
|
||||||
use Illuminate\Support\ServiceProvider;
|
use Illuminate\Support\ServiceProvider;
|
||||||
|
|
||||||
class MicropubServiceProvider extends ServiceProvider
|
class MicropubServiceProvider extends ServiceProvider
|
||||||
|
|
@ -19,6 +20,7 @@ class MicropubServiceProvider extends ServiceProvider
|
||||||
// Register handlers
|
// Register handlers
|
||||||
$registry->register('card', new CardHandler);
|
$registry->register('card', new CardHandler);
|
||||||
$registry->register('entry', new EntryHandler);
|
$registry->register('entry', new EntryHandler);
|
||||||
|
$registry->register('update', new UpdateHandler);
|
||||||
|
|
||||||
return $registry;
|
return $registry;
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -5,21 +5,21 @@ declare(strict_types=1);
|
||||||
namespace App\Services\Micropub;
|
namespace App\Services\Micropub;
|
||||||
|
|
||||||
use App\Exceptions\InvalidTokenScopeException;
|
use App\Exceptions\InvalidTokenScopeException;
|
||||||
|
use App\Exceptions\MicropubHandlerException;
|
||||||
|
use App\Exceptions\MicropubUnsupportedModelException;
|
||||||
use App\Models\Media;
|
use App\Models\Media;
|
||||||
use App\Models\Note;
|
use App\Models\Note;
|
||||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
|
||||||
use Illuminate\Support\Arr;
|
use Illuminate\Support\Arr;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
/*
|
|
||||||
* @todo Implement this properly
|
|
||||||
*/
|
|
||||||
class UpdateHandler implements MicropubHandlerInterface
|
class UpdateHandler implements MicropubHandlerInterface
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @throws InvalidTokenScopeException
|
* @throws InvalidTokenScopeException
|
||||||
|
* @throws MicropubUnsupportedModelException
|
||||||
|
* @throws MicropubHandlerException
|
||||||
*/
|
*/
|
||||||
public function handle(array $data)
|
public function handle(array $data): array
|
||||||
{
|
{
|
||||||
$scopes = $data['token_data']['scope'];
|
$scopes = $data['token_data']['scope'];
|
||||||
if (is_string($scopes)) {
|
if (is_string($scopes)) {
|
||||||
|
|
@ -30,67 +30,35 @@ class UpdateHandler implements MicropubHandlerInterface
|
||||||
throw new InvalidTokenScopeException;
|
throw new InvalidTokenScopeException;
|
||||||
}
|
}
|
||||||
|
|
||||||
$urlPath = parse_url(Arr::get($data, 'url'), PHP_URL_PATH);
|
$urlPath = parse_url(Arr::get($data, 'update_url'), PHP_URL_PATH);
|
||||||
|
|
||||||
// is it a note we are updating?
|
|
||||||
if (mb_substr($urlPath, 1, 5) !== 'notes') {
|
if (mb_substr($urlPath, 1, 5) !== 'notes') {
|
||||||
return response()->json([
|
throw new MicropubUnsupportedModelException('This implementation currently only supports the updating of notes');
|
||||||
'error' => 'invalid',
|
|
||||||
'error_description' => 'This implementation currently only support the updating of notes',
|
|
||||||
], 500);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
$note = Note::nb60(basename($urlPath))->firstOrFail();
|
||||||
$note = Note::nb60(basename($urlPath))->firstOrFail();
|
|
||||||
} catch (ModelNotFoundException) {
|
|
||||||
return response()->json([
|
|
||||||
'error' => 'invalid_request',
|
|
||||||
'error_description' => 'No known note with given ID',
|
|
||||||
], 404);
|
|
||||||
}
|
|
||||||
|
|
||||||
// got the note, are we dealing with a “replace” request?
|
if (Arr::get($data, 'update_replace')) {
|
||||||
if (Arr::get($data, 'replace')) {
|
foreach (Arr::get($data, 'update_replace') as $property => $value) {
|
||||||
foreach (Arr::get($data, 'replace') as $property => $value) {
|
|
||||||
if ($property === 'content') {
|
if ($property === 'content') {
|
||||||
$note->note = $value[0];
|
$note->note = $value[0];
|
||||||
}
|
}
|
||||||
if ($property === 'syndication') {
|
if ($property === 'syndication') {
|
||||||
foreach ($value as $syndicationURL) {
|
$this->applySyndication($note, $value);
|
||||||
if (Str::startsWith($syndicationURL, 'https://www.facebook.com')) {
|
|
||||||
$note->facebook_url = $syndicationURL;
|
|
||||||
}
|
|
||||||
if (Str::startsWith($syndicationURL, 'https://www.swarmapp.com')) {
|
|
||||||
$note->swarm_url = $syndicationURL;
|
|
||||||
}
|
|
||||||
if (Str::startsWith($syndicationURL, 'https://twitter.com')) {
|
|
||||||
$note->tweet_id = basename(parse_url($syndicationURL, PHP_URL_PATH));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$note->save();
|
$note->save();
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'response' => 'updated',
|
'response' => 'updated',
|
||||||
|
'url' => $note->uri,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
// how about “add”
|
if (Arr::get($data, 'update_add')) {
|
||||||
if (Arr::get($data, 'add')) {
|
foreach (Arr::get($data, 'update_add') as $property => $value) {
|
||||||
foreach (Arr::get($data, 'add') as $property => $value) {
|
|
||||||
if ($property === 'syndication') {
|
if ($property === 'syndication') {
|
||||||
foreach ($value as $syndicationURL) {
|
$this->applySyndication($note, $value);
|
||||||
if (Str::startsWith($syndicationURL, 'https://www.facebook.com')) {
|
|
||||||
$note->facebook_url = $syndicationURL;
|
|
||||||
}
|
|
||||||
if (Str::startsWith($syndicationURL, 'https://www.swarmapp.com')) {
|
|
||||||
$note->swarm_url = $syndicationURL;
|
|
||||||
}
|
|
||||||
if (Str::startsWith($syndicationURL, 'https://twitter.com')) {
|
|
||||||
$note->tweet_id = basename(parse_url($syndicationURL, PHP_URL_PATH));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if ($property === 'photo') {
|
if ($property === 'photo') {
|
||||||
foreach ($value as $photoURL) {
|
foreach ($value as $photoURL) {
|
||||||
|
|
@ -106,14 +74,25 @@ class UpdateHandler implements MicropubHandlerInterface
|
||||||
}
|
}
|
||||||
$note->save();
|
$note->save();
|
||||||
|
|
||||||
return response()->json([
|
return [
|
||||||
'response' => 'updated',
|
'response' => 'updated',
|
||||||
]);
|
'url' => $note->uri,
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
return response()->json([
|
throw new MicropubHandlerException('Unsupported update operation');
|
||||||
'response' => 'error',
|
}
|
||||||
'error_description' => 'unsupported request',
|
|
||||||
], 500);
|
private function applySyndication(Note $note, array $urls): void
|
||||||
|
{
|
||||||
|
foreach ($urls as $url) {
|
||||||
|
if (Str::startsWith($url, 'https://www.facebook.com')) {
|
||||||
|
$note->facebook_url = $url;
|
||||||
|
} elseif (Str::startsWith($url, 'https://www.swarmapp.com')) {
|
||||||
|
$note->swarm_url = $url;
|
||||||
|
} elseif (Str::startsWith($url, 'https://twitter.com')) {
|
||||||
|
$note->tweet_id = basename(parse_url($url, PHP_URL_PATH));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -228,8 +228,8 @@ class MicropubControllerTest extends TestCase
|
||||||
],
|
],
|
||||||
['HTTP_Authorization' => 'Bearer ' . $this->getTokenWithIncorrectScope()]
|
['HTTP_Authorization' => 'Bearer ' . $this->getTokenWithIncorrectScope()]
|
||||||
);
|
);
|
||||||
$response->assertStatus(403);
|
$response->assertStatus(401);
|
||||||
$response->assertJson(['error' => 'invalid_scope']);
|
$response->assertJson(['error' => 'insufficient_scope']);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -448,10 +448,10 @@ class MicropubControllerTest extends TestCase
|
||||||
);
|
);
|
||||||
$response
|
$response
|
||||||
->assertJson([
|
->assertJson([
|
||||||
'error' => 'invalid_scope',
|
'error' => 'insufficient_scope',
|
||||||
'error_description' => 'The token does not have the required scope for this request',
|
'error_description' => 'The token does not have the required scope for this request',
|
||||||
])
|
])
|
||||||
->assertStatus(403);
|
->assertStatus(401);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[Test]
|
#[Test]
|
||||||
|
|
@ -469,7 +469,7 @@ class MicropubControllerTest extends TestCase
|
||||||
);
|
);
|
||||||
$response
|
$response
|
||||||
->assertJson([
|
->assertJson([
|
||||||
'error' => 'Unknown Micropub type',
|
'error' => 'unsupported_operation',
|
||||||
'error_description' => 'The request could not be processed by this server',
|
'error_description' => 'The request could not be processed by this server',
|
||||||
])
|
])
|
||||||
->assertStatus(500);
|
->assertStatus(500);
|
||||||
|
|
@ -518,8 +518,6 @@ class MicropubControllerTest extends TestCase
|
||||||
#[Test]
|
#[Test]
|
||||||
public function micropub_client_api_request_updates_existing_note(): void
|
public function micropub_client_api_request_updates_existing_note(): void
|
||||||
{
|
{
|
||||||
$this->markTestSkipped('Update requests are not supported yet');
|
|
||||||
|
|
||||||
$note = Note::factory()->create();
|
$note = Note::factory()->create();
|
||||||
$response = $this->postJson(
|
$response = $this->postJson(
|
||||||
'/api/post',
|
'/api/post',
|
||||||
|
|
@ -534,14 +532,15 @@ class MicropubControllerTest extends TestCase
|
||||||
);
|
);
|
||||||
$response
|
$response
|
||||||
->assertJson(['response' => 'updated'])
|
->assertJson(['response' => 'updated'])
|
||||||
->assertStatus(200);
|
->assertSuccessful();
|
||||||
|
|
||||||
|
$note->refresh();
|
||||||
|
$this->assertSame('replaced content', $note->content);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[Test]
|
#[Test]
|
||||||
public function micropub_client_api_request_updates_note_syndication_links(): void
|
public function micropub_client_api_request_updates_note_syndication_links(): void
|
||||||
{
|
{
|
||||||
$this->markTestSkipped('Update requests are not supported yet');
|
|
||||||
|
|
||||||
$note = Note::factory()->create();
|
$note = Note::factory()->create();
|
||||||
$response = $this->postJson(
|
$response = $this->postJson(
|
||||||
'/api/post',
|
'/api/post',
|
||||||
|
|
@ -559,7 +558,8 @@ class MicropubControllerTest extends TestCase
|
||||||
);
|
);
|
||||||
$response
|
$response
|
||||||
->assertJson(['response' => 'updated'])
|
->assertJson(['response' => 'updated'])
|
||||||
->assertStatus(200);
|
->assertSuccessful();
|
||||||
|
|
||||||
$this->assertDatabaseHas('notes', [
|
$this->assertDatabaseHas('notes', [
|
||||||
'swarm_url' => 'https://www.swarmapp.com/checkin/123',
|
'swarm_url' => 'https://www.swarmapp.com/checkin/123',
|
||||||
'facebook_url' => 'https://www.facebook.com/checkin/123',
|
'facebook_url' => 'https://www.facebook.com/checkin/123',
|
||||||
|
|
@ -569,8 +569,6 @@ class MicropubControllerTest extends TestCase
|
||||||
#[Test]
|
#[Test]
|
||||||
public function micropub_client_api_request_adds_image_to_note(): void
|
public function micropub_client_api_request_adds_image_to_note(): void
|
||||||
{
|
{
|
||||||
$this->markTestSkipped('Update requests are not supported yet');
|
|
||||||
|
|
||||||
$note = Note::factory()->create();
|
$note = Note::factory()->create();
|
||||||
$response = $this->postJson(
|
$response = $this->postJson(
|
||||||
'/api/post',
|
'/api/post',
|
||||||
|
|
@ -585,7 +583,7 @@ class MicropubControllerTest extends TestCase
|
||||||
);
|
);
|
||||||
$response
|
$response
|
||||||
->assertJson(['response' => 'updated'])
|
->assertJson(['response' => 'updated'])
|
||||||
->assertStatus(200);
|
->assertSuccessful();
|
||||||
$this->assertDatabaseHas('media_endpoint', [
|
$this->assertDatabaseHas('media_endpoint', [
|
||||||
'path' => 'https://example.org/photo.jpg',
|
'path' => 'https://example.org/photo.jpg',
|
||||||
]);
|
]);
|
||||||
|
|
@ -594,8 +592,6 @@ class MicropubControllerTest extends TestCase
|
||||||
#[Test]
|
#[Test]
|
||||||
public function micropub_client_api_request_returns_error_trying_to_update_non_note_model(): void
|
public function micropub_client_api_request_returns_error_trying_to_update_non_note_model(): void
|
||||||
{
|
{
|
||||||
$this->markTestSkipped('Update requests are not supported yet');
|
|
||||||
|
|
||||||
$response = $this->postJson(
|
$response = $this->postJson(
|
||||||
'/api/post',
|
'/api/post',
|
||||||
[
|
[
|
||||||
|
|
@ -615,8 +611,6 @@ class MicropubControllerTest extends TestCase
|
||||||
#[Test]
|
#[Test]
|
||||||
public function micropub_client_api_request_returns_error_trying_to_update_non_existing_note(): void
|
public function micropub_client_api_request_returns_error_trying_to_update_non_existing_note(): void
|
||||||
{
|
{
|
||||||
$this->markTestSkipped('Update requests are not supported yet');
|
|
||||||
|
|
||||||
$response = $this->postJson(
|
$response = $this->postJson(
|
||||||
'/api/post',
|
'/api/post',
|
||||||
[
|
[
|
||||||
|
|
@ -636,8 +630,6 @@ class MicropubControllerTest extends TestCase
|
||||||
#[Test]
|
#[Test]
|
||||||
public function micropub_client_api_request_returns_error_when_trying_to_update_unsupported_property(): void
|
public function micropub_client_api_request_returns_error_when_trying_to_update_unsupported_property(): void
|
||||||
{
|
{
|
||||||
$this->markTestSkipped('Update requests are not supported yet');
|
|
||||||
|
|
||||||
$note = Note::factory()->create();
|
$note = Note::factory()->create();
|
||||||
$response = $this->postJson(
|
$response = $this->postJson(
|
||||||
'/api/post',
|
'/api/post',
|
||||||
|
|
@ -651,15 +643,13 @@ class MicropubControllerTest extends TestCase
|
||||||
['HTTP_Authorization' => 'Bearer ' . $this->getToken()]
|
['HTTP_Authorization' => 'Bearer ' . $this->getToken()]
|
||||||
);
|
);
|
||||||
$response
|
$response
|
||||||
->assertJson(['response' => 'error'])
|
->assertJson(['error' => 'unsupported_operation'])
|
||||||
->assertStatus(500);
|
->assertStatus(500);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[Test]
|
#[Test]
|
||||||
public function micropub_client_api_request_with_token_with_insufficient_scope_returns_error(): void
|
public function micropub_client_api_request_with_token_with_insufficient_scope_returns_error(): void
|
||||||
{
|
{
|
||||||
$this->markTestSkipped('Update requests are not supported yet');
|
|
||||||
|
|
||||||
$response = $this->postJson(
|
$response = $this->postJson(
|
||||||
'/api/post',
|
'/api/post',
|
||||||
[
|
[
|
||||||
|
|
@ -679,8 +669,6 @@ class MicropubControllerTest extends TestCase
|
||||||
#[Test]
|
#[Test]
|
||||||
public function micropub_client_api_request_can_replace_note_syndication_targets(): void
|
public function micropub_client_api_request_can_replace_note_syndication_targets(): void
|
||||||
{
|
{
|
||||||
$this->markTestSkipped('Update requests are not supported yet');
|
|
||||||
|
|
||||||
$note = Note::factory()->create();
|
$note = Note::factory()->create();
|
||||||
$response = $this->postJson(
|
$response = $this->postJson(
|
||||||
'/api/post',
|
'/api/post',
|
||||||
|
|
@ -698,7 +686,7 @@ class MicropubControllerTest extends TestCase
|
||||||
);
|
);
|
||||||
$response
|
$response
|
||||||
->assertJson(['response' => 'updated'])
|
->assertJson(['response' => 'updated'])
|
||||||
->assertStatus(200);
|
->assertSuccessful();
|
||||||
$this->assertDatabaseHas('notes', [
|
$this->assertDatabaseHas('notes', [
|
||||||
'swarm_url' => 'https://www.swarmapp.com/checkin/the-id',
|
'swarm_url' => 'https://www.swarmapp.com/checkin/the-id',
|
||||||
'facebook_url' => 'https://www.facebook.com/post/the-id',
|
'facebook_url' => 'https://www.facebook.com/post/the-id',
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue