Move request parsing into DTOs via fromRequest(), expand update handler
Each DTO now owns its own parsing logic via fromRequest(), removing the normalization layer from MicropubRequest entirely. UpdateHandler gains delete support and allows replace/add/delete to compose in a single request rather than being mutually exclusive. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
ad15b36f4d
commit
1137d2a07e
8 changed files with 310 additions and 124 deletions
|
|
@ -10,6 +10,7 @@ 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\Data\MicropubData;
|
||||||
use App\Services\Micropub\MicropubHandlerRegistry;
|
use App\Services\Micropub\MicropubHandlerRegistry;
|
||||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||||
use Illuminate\Http\JsonResponse;
|
use Illuminate\Http\JsonResponse;
|
||||||
|
|
@ -28,9 +29,9 @@ class MicropubController extends Controller
|
||||||
/**
|
/**
|
||||||
* Respond to a POST request to the micropub endpoint.
|
* Respond to a POST request to the micropub endpoint.
|
||||||
*
|
*
|
||||||
* The request is initially processed by the MicropubRequest form request
|
* MicropubRequest detects the request type (e.g. entry, card, update).
|
||||||
* class. The normalizes the data, so we can pass it into the handlers for
|
* The handler registry resolves the appropriate handler, whose DTO
|
||||||
* the different micropub requests, h-entry or h-card, for example.
|
* extracts the relevant fields from the request via fromRequest().
|
||||||
*/
|
*/
|
||||||
public function post(MicropubRequest $request): JsonResponse
|
public function post(MicropubRequest $request): JsonResponse
|
||||||
{
|
{
|
||||||
|
|
@ -46,7 +47,8 @@ class MicropubController extends Controller
|
||||||
try {
|
try {
|
||||||
$handler = $this->handlerRegistry->getHandler($type);
|
$handler = $this->handlerRegistry->getHandler($type);
|
||||||
$dataClass = $handler->dataClass();
|
$dataClass = $handler->dataClass();
|
||||||
$data = $dataClass::fromArray($request->getMicropubData());
|
/** @var MicropubData $data */
|
||||||
|
$data = $dataClass::fromRequest($request);
|
||||||
$result = $handler->handle($data);
|
$result = $handler->handle($data);
|
||||||
|
|
||||||
if ($result['response'] === 'updated') {
|
if ($result['response'] === 'updated') {
|
||||||
|
|
@ -95,10 +97,9 @@ class MicropubController extends Controller
|
||||||
/**
|
/**
|
||||||
* Respond to a GET request to the micropub endpoint.
|
* Respond to a GET request to the micropub endpoint.
|
||||||
*
|
*
|
||||||
* A GET request has been made to `api/post` with an accompanying
|
* Token validation is handled by the VerifyMicropubToken middleware.
|
||||||
* token, here we check whether the token is valid and respond
|
* Supports q=syndicate-to, q=config, and q=geo:<lat>,<lng> queries.
|
||||||
* appropriately. Further if the request has the query parameter
|
* The default response returns the token metadata.
|
||||||
* syndicate-to we respond with the known syndication endpoints.
|
|
||||||
*/
|
*/
|
||||||
public function get(Request $request): JsonResponse
|
public function get(Request $request): JsonResponse
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -5,12 +5,9 @@ declare(strict_types=1);
|
||||||
namespace App\Http\Requests;
|
namespace App\Http\Requests;
|
||||||
|
|
||||||
use Illuminate\Foundation\Http\FormRequest;
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
use Illuminate\Support\Arr;
|
|
||||||
|
|
||||||
class MicropubRequest extends FormRequest
|
class MicropubRequest extends FormRequest
|
||||||
{
|
{
|
||||||
protected array $micropubData = [];
|
|
||||||
|
|
||||||
public function rules(): array
|
public function rules(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
|
|
@ -18,110 +15,25 @@ class MicropubRequest extends FormRequest
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getMicropubData(): array
|
|
||||||
{
|
|
||||||
return $this->micropubData;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getType(): ?string
|
public function getType(): ?string
|
||||||
{
|
{
|
||||||
// Return consistent type regardless of input format
|
|
||||||
return $this->micropubData['type'] ?? null;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function prepareForValidation(): void
|
|
||||||
{
|
|
||||||
// Normalize the request data based on content type
|
|
||||||
if ($this->isJson()) {
|
if ($this->isJson()) {
|
||||||
$this->normalizeMicropubJson();
|
$data = $this->json()->all();
|
||||||
} else {
|
|
||||||
$this->normalizeMicropubForm();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function normalizeMicropubJson(): void
|
|
||||||
{
|
|
||||||
$json = $this->json();
|
|
||||||
if ($json === null) {
|
|
||||||
throw new \InvalidArgumentException('`isJson()` passed but there is no json data');
|
|
||||||
}
|
|
||||||
|
|
||||||
$data = $json->all();
|
|
||||||
|
|
||||||
$this->micropubData['token_data'] = $data['token_data'];
|
|
||||||
|
|
||||||
if (isset($data['action']) && $data['action'] === 'update') {
|
if (isset($data['action']) && $data['action'] === 'update') {
|
||||||
$this->micropubData['type'] = 'update';
|
return '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);
|
return substr($type, 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add h-entry values
|
|
||||||
$this->micropubData['content'] = Arr::get($data, 'properties.content.0');
|
|
||||||
$this->micropubData['in-reply-to'] = Arr::get($data, 'properties.in-reply-to.0');
|
|
||||||
$this->micropubData['published'] = Arr::get($data, 'properties.published.0');
|
|
||||||
$this->micropubData['location'] = $this->getLocationData($data);
|
|
||||||
$this->micropubData['bookmark-of'] = Arr::get($data, 'properties.bookmark-of.0');
|
|
||||||
$this->micropubData['like-of'] = Arr::get($data, 'properties.like-of.0');
|
|
||||||
$this->micropubData['mp-syndicate-to'] = Arr::get($data, 'properties.mp-syndicate-to');
|
|
||||||
|
|
||||||
// Add h-card values
|
|
||||||
$this->micropubData['name'] = Arr::get($data, 'properties.name.0');
|
|
||||||
$this->micropubData['description'] = Arr::get($data, 'properties.description.0');
|
|
||||||
$this->micropubData['geo'] = Arr::get($data, 'properties.geo.0');
|
|
||||||
|
|
||||||
// Add checkin value
|
|
||||||
$this->micropubData['checkin'] = Arr::get($data, 'checkin');
|
|
||||||
$this->micropubData['syndication'] = Arr::get($data, 'properties.syndication.0');
|
|
||||||
|
|
||||||
// Add photos
|
|
||||||
$this->micropubData['photos'] = Arr::get($data, 'properties.photo');
|
|
||||||
}
|
|
||||||
|
|
||||||
private function normalizeMicropubForm(): void
|
|
||||||
{
|
|
||||||
// Convert form h=entry to type=entry
|
|
||||||
if ($h = $this->input('h')) {
|
|
||||||
$this->micropubData['type'] = $h;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add some fields to the micropub data with default null values
|
|
||||||
$this->micropubData['in-reply-to'] = null;
|
|
||||||
$this->micropubData['published'] = null;
|
|
||||||
$this->micropubData['location'] = null;
|
|
||||||
$this->micropubData['description'] = null;
|
|
||||||
$this->micropubData['geo'] = null;
|
|
||||||
$this->micropubData['latitude'] = null;
|
|
||||||
$this->micropubData['longitude'] = null;
|
|
||||||
|
|
||||||
// Map form fields to micropub data
|
|
||||||
foreach ($this->except(['h', 'access_token']) as $key => $value) {
|
|
||||||
$this->micropubData[$key] = $value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getLocationData(array $data): array|string|null
|
|
||||||
{
|
|
||||||
if (! Arr::has($data, 'properties.location')) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Arr::has($data, 'properties.location.0')) {
|
return $this->input('h') ?: null;
|
||||||
return Arr::get($data, 'properties.location.0');
|
|
||||||
}
|
|
||||||
|
|
||||||
return Arr::get($data, 'properties.location');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,9 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\Services\Micropub\Data;
|
namespace App\Services\Micropub\Data;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Arr;
|
||||||
|
|
||||||
class CardData extends MicropubData
|
class CardData extends MicropubData
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
|
|
@ -16,6 +19,33 @@ class CardData extends MicropubData
|
||||||
public readonly ?string $longitude,
|
public readonly ?string $longitude,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
public static function fromRequest(Request $request): static
|
||||||
|
{
|
||||||
|
if ($request->isJson()) {
|
||||||
|
$data = $request->json()->all();
|
||||||
|
|
||||||
|
return new static(
|
||||||
|
tokenData: $data['token_data'],
|
||||||
|
name: Arr::get($data, 'properties.name.0'),
|
||||||
|
description: Arr::get($data, 'properties.description.0'),
|
||||||
|
geo: Arr::get($data, 'properties.geo.0'),
|
||||||
|
location: Arr::get($data, 'properties.location'),
|
||||||
|
latitude: null,
|
||||||
|
longitude: null,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new static(
|
||||||
|
tokenData: $request->input('token_data'),
|
||||||
|
name: $request->input('name'),
|
||||||
|
description: $request->input('description'),
|
||||||
|
geo: $request->input('geo'),
|
||||||
|
location: $request->input('location'),
|
||||||
|
latitude: $request->input('latitude'),
|
||||||
|
longitude: $request->input('longitude'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public static function fromArray(array $data): static
|
public static function fromArray(array $data): static
|
||||||
{
|
{
|
||||||
return new static(
|
return new static(
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,9 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\Services\Micropub\Data;
|
namespace App\Services\Micropub\Data;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Arr;
|
||||||
|
|
||||||
class EntryData extends MicropubData
|
class EntryData extends MicropubData
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
|
|
@ -23,6 +26,63 @@ class EntryData extends MicropubData
|
||||||
public readonly ?array $photos,
|
public readonly ?array $photos,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
public static function fromRequest(Request $request): static
|
||||||
|
{
|
||||||
|
if ($request->isJson()) {
|
||||||
|
$data = $request->json()->all();
|
||||||
|
|
||||||
|
$rawContent = Arr::get($data, 'properties.content.0');
|
||||||
|
$content = is_array($rawContent) ? ($rawContent['html'] ?? $rawContent['value'] ?? null) : $rawContent;
|
||||||
|
|
||||||
|
return new static(
|
||||||
|
tokenData: $data['token_data'],
|
||||||
|
content: $content,
|
||||||
|
inReplyTo: Arr::get($data, 'properties.in-reply-to.0'),
|
||||||
|
published: Arr::get($data, 'properties.published.0'),
|
||||||
|
location: self::extractLocationData($data),
|
||||||
|
bookmarkOf: Arr::get($data, 'properties.bookmark-of.0'),
|
||||||
|
likeOf: Arr::get($data, 'properties.like-of.0'),
|
||||||
|
mpSyndicateTo: Arr::get($data, 'properties.mp-syndicate-to'),
|
||||||
|
name: Arr::get($data, 'properties.name.0'),
|
||||||
|
description: Arr::get($data, 'properties.description.0'),
|
||||||
|
geo: Arr::get($data, 'properties.geo.0'),
|
||||||
|
checkin: Arr::get($data, 'checkin'),
|
||||||
|
syndication: Arr::get($data, 'properties.syndication.0'),
|
||||||
|
photos: Arr::get($data, 'properties.photo'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new static(
|
||||||
|
tokenData: $request->input('token_data'),
|
||||||
|
content: $request->input('content'),
|
||||||
|
inReplyTo: $request->input('in-reply-to'),
|
||||||
|
published: $request->input('published'),
|
||||||
|
location: $request->input('location'),
|
||||||
|
bookmarkOf: $request->input('bookmark-of'),
|
||||||
|
likeOf: $request->input('like-of'),
|
||||||
|
mpSyndicateTo: $request->input('mp-syndicate-to'),
|
||||||
|
name: $request->input('name'),
|
||||||
|
description: $request->input('description'),
|
||||||
|
geo: $request->input('geo'),
|
||||||
|
checkin: $request->input('checkin'),
|
||||||
|
syndication: $request->input('syndication'),
|
||||||
|
photos: $request->input('photos'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function extractLocationData(array $data): array|string|null
|
||||||
|
{
|
||||||
|
if (! Arr::has($data, 'properties.location')) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Arr::has($data, 'properties.location.0')) {
|
||||||
|
return Arr::get($data, 'properties.location.0');
|
||||||
|
}
|
||||||
|
|
||||||
|
return Arr::get($data, 'properties.location');
|
||||||
|
}
|
||||||
|
|
||||||
public static function fromArray(array $data): static
|
public static function fromArray(array $data): static
|
||||||
{
|
{
|
||||||
return new static(
|
return new static(
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,12 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\Services\Micropub\Data;
|
namespace App\Services\Micropub\Data;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
abstract class MicropubData
|
abstract class MicropubData
|
||||||
{
|
{
|
||||||
|
abstract public static function fromRequest(Request $request): static;
|
||||||
|
|
||||||
abstract public static function fromArray(array $data): static;
|
abstract public static function fromArray(array $data): static;
|
||||||
|
|
||||||
abstract public function toArray(): array;
|
abstract public function toArray(): array;
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,8 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\Services\Micropub\Data;
|
namespace App\Services\Micropub\Data;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
class UpdateData extends MicropubData
|
class UpdateData extends MicropubData
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
|
|
@ -14,6 +16,29 @@ class UpdateData extends MicropubData
|
||||||
public readonly ?array $updateDelete,
|
public readonly ?array $updateDelete,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
public static function fromRequest(Request $request): static
|
||||||
|
{
|
||||||
|
if ($request->isJson()) {
|
||||||
|
$data = $request->json()->all();
|
||||||
|
|
||||||
|
return new static(
|
||||||
|
tokenData: $data['token_data'],
|
||||||
|
updateUrl: $data['url'] ?? null,
|
||||||
|
updateReplace: $data['replace'] ?? null,
|
||||||
|
updateAdd: $data['add'] ?? null,
|
||||||
|
updateDelete: $data['delete'] ?? null,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new static(
|
||||||
|
tokenData: $request->input('token_data'),
|
||||||
|
updateUrl: $request->input('url'),
|
||||||
|
updateReplace: $request->input('replace'),
|
||||||
|
updateAdd: $request->input('add'),
|
||||||
|
updateDelete: $request->input('delete'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public static function fromArray(array $data): static
|
public static function fromArray(array $data): static
|
||||||
{
|
{
|
||||||
return new static(
|
return new static(
|
||||||
|
|
|
||||||
|
|
@ -46,21 +46,18 @@ class UpdateHandler implements MicropubHandlerInterface
|
||||||
|
|
||||||
$note = Note::nb60(basename($urlPath))->firstOrFail();
|
$note = Note::nb60(basename($urlPath))->firstOrFail();
|
||||||
|
|
||||||
|
if ($data->updateReplace === null && $data->updateAdd === null && $data->updateDelete === null) {
|
||||||
|
throw new MicropubHandlerException('Unsupported update operation');
|
||||||
|
}
|
||||||
|
|
||||||
if ($data->updateReplace !== null) {
|
if ($data->updateReplace !== null) {
|
||||||
foreach ($data->updateReplace as $property => $value) {
|
foreach ($data->updateReplace as $property => $value) {
|
||||||
if ($property === 'content') {
|
match ($property) {
|
||||||
$note->note = $value[0];
|
'content' => $note->note = $value[0],
|
||||||
|
'syndication' => $this->applySyndication($note, $value),
|
||||||
|
default => null,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
if ($property === 'syndication') {
|
|
||||||
$this->applySyndication($note, $value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$note->save();
|
|
||||||
|
|
||||||
return [
|
|
||||||
'response' => 'updated',
|
|
||||||
'url' => $note->uri,
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($data->updateAdd !== null) {
|
if ($data->updateAdd !== null) {
|
||||||
|
|
@ -80,6 +77,12 @@ class UpdateHandler implements MicropubHandlerInterface
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($data->updateDelete !== null) {
|
||||||
|
$this->applyDelete($note, $data->updateDelete);
|
||||||
|
}
|
||||||
|
|
||||||
$note->save();
|
$note->save();
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
|
@ -88,7 +91,48 @@ class UpdateHandler implements MicropubHandlerInterface
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new MicropubHandlerException('Unsupported update operation');
|
private function applyDelete(Note $note, array $delete): void
|
||||||
|
{
|
||||||
|
if (array_is_list($delete)) {
|
||||||
|
foreach ($delete as $property) {
|
||||||
|
match ($property) {
|
||||||
|
'syndication' => $this->clearSyndication($note),
|
||||||
|
'photo' => $note->media()->delete(),
|
||||||
|
default => null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($delete as $property => $values) {
|
||||||
|
if ($property === 'syndication') {
|
||||||
|
$this->removeSyndicationValues($note, $values);
|
||||||
|
}
|
||||||
|
if ($property === 'photo') {
|
||||||
|
$note->media()->whereIn('path', $values)->delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function clearSyndication(Note $note): void
|
||||||
|
{
|
||||||
|
$note->facebook_url = null;
|
||||||
|
$note->swarm_url = null;
|
||||||
|
$note->tweet_id = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function removeSyndicationValues(Note $note, array $urls): void
|
||||||
|
{
|
||||||
|
foreach ($urls as $url) {
|
||||||
|
if (Str::startsWith($url, 'https://www.facebook.com') && $note->facebook_url === $url) {
|
||||||
|
$note->facebook_url = null;
|
||||||
|
} elseif (Str::startsWith($url, 'https://www.swarmapp.com') && $note->swarm_url === $url) {
|
||||||
|
$note->swarm_url = null;
|
||||||
|
} elseif (Str::startsWith($url, 'https://twitter.com')) {
|
||||||
|
$note->tweet_id = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function applySyndication(Note $note, array $urls): void
|
private function applySyndication(Note $note, array $urls): void
|
||||||
|
|
|
||||||
|
|
@ -711,6 +711,116 @@ class MicropubControllerTest extends TestCase
|
||||||
$this->assertDatabaseHas('notes', ['note' => $note]);
|
$this->assertDatabaseHas('notes', ['note' => $note]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[Test]
|
||||||
|
public function micropub_client_api_request_can_combine_replace_and_add_in_single_update(): void
|
||||||
|
{
|
||||||
|
$note = Note::factory()->create();
|
||||||
|
$response = $this->postJson(
|
||||||
|
'/api/post',
|
||||||
|
[
|
||||||
|
'action' => 'update',
|
||||||
|
'url' => $note->uri,
|
||||||
|
'replace' => [
|
||||||
|
'content' => ['replaced content'],
|
||||||
|
],
|
||||||
|
'add' => [
|
||||||
|
'syndication' => ['https://www.swarmapp.com/checkin/123'],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
['HTTP_Authorization' => 'Bearer ' . $this->getToken()]
|
||||||
|
);
|
||||||
|
$response
|
||||||
|
->assertJson(['response' => 'updated'])
|
||||||
|
->assertSuccessful();
|
||||||
|
|
||||||
|
$note->refresh();
|
||||||
|
$this->assertSame('replaced content', $note->content);
|
||||||
|
$this->assertDatabaseHas('notes', [
|
||||||
|
'swarm_url' => 'https://www.swarmapp.com/checkin/123',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Test]
|
||||||
|
public function micropub_client_api_request_can_delete_entire_property(): void
|
||||||
|
{
|
||||||
|
$note = Note::factory()->create([
|
||||||
|
'swarm_url' => 'https://www.swarmapp.com/checkin/123',
|
||||||
|
'facebook_url' => 'https://www.facebook.com/post/123',
|
||||||
|
]);
|
||||||
|
$response = $this->postJson(
|
||||||
|
'/api/post',
|
||||||
|
[
|
||||||
|
'action' => 'update',
|
||||||
|
'url' => $note->uri,
|
||||||
|
'delete' => ['syndication'],
|
||||||
|
],
|
||||||
|
['HTTP_Authorization' => 'Bearer ' . $this->getToken()]
|
||||||
|
);
|
||||||
|
$response
|
||||||
|
->assertJson(['response' => 'updated'])
|
||||||
|
->assertSuccessful();
|
||||||
|
|
||||||
|
$note->refresh();
|
||||||
|
$this->assertNull($note->swarm_url);
|
||||||
|
$this->assertNull($note->facebook_url);
|
||||||
|
$this->assertNull($note->tweet_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Test]
|
||||||
|
public function micropub_client_api_request_can_delete_specific_syndication_value(): void
|
||||||
|
{
|
||||||
|
$note = Note::factory()->create([
|
||||||
|
'swarm_url' => 'https://www.swarmapp.com/checkin/123',
|
||||||
|
'facebook_url' => 'https://www.facebook.com/post/123',
|
||||||
|
]);
|
||||||
|
$response = $this->postJson(
|
||||||
|
'/api/post',
|
||||||
|
[
|
||||||
|
'action' => 'update',
|
||||||
|
'url' => $note->uri,
|
||||||
|
'delete' => [
|
||||||
|
'syndication' => ['https://www.swarmapp.com/checkin/123'],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
['HTTP_Authorization' => 'Bearer ' . $this->getToken()]
|
||||||
|
);
|
||||||
|
$response
|
||||||
|
->assertJson(['response' => 'updated'])
|
||||||
|
->assertSuccessful();
|
||||||
|
|
||||||
|
$note->refresh();
|
||||||
|
$this->assertNull($note->swarm_url);
|
||||||
|
$this->assertSame('https://www.facebook.com/post/123', $note->facebook_url);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Test]
|
||||||
|
public function micropub_client_api_request_can_delete_photo(): void
|
||||||
|
{
|
||||||
|
$note = Note::factory()->create();
|
||||||
|
$media = new \App\Models\Media;
|
||||||
|
$media->path = 'https://example.org/photo.jpg';
|
||||||
|
$media->type = 'image';
|
||||||
|
$media->save();
|
||||||
|
$note->media()->save($media);
|
||||||
|
|
||||||
|
$response = $this->postJson(
|
||||||
|
'/api/post',
|
||||||
|
[
|
||||||
|
'action' => 'update',
|
||||||
|
'url' => $note->uri,
|
||||||
|
'delete' => ['photo'],
|
||||||
|
],
|
||||||
|
['HTTP_Authorization' => 'Bearer ' . $this->getToken()]
|
||||||
|
);
|
||||||
|
$response
|
||||||
|
->assertJson(['response' => 'updated'])
|
||||||
|
->assertSuccessful();
|
||||||
|
|
||||||
|
$this->assertDatabaseMissing('media_endpoint', [
|
||||||
|
'path' => 'https://example.org/photo.jpg',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
#[Test]
|
#[Test]
|
||||||
public function micropub_client_api_request_creates_articles_when_it_includes_the_name_property(): void
|
public function micropub_client_api_request_creates_articles_when_it_includes_the_name_property(): void
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue