fix(api): use DeleteLink action for cache invalidation, fix validation, add delete tests

main
boban 2026-05-16 07:42:56 +02:00
parent 1375519cdb
commit 3568e67ca0
4 changed files with 41 additions and 6 deletions

View File

@ -2,11 +2,12 @@
namespace App\Domains\Api\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class LinkResource extends JsonResource
{
public function toArray($request): array
public function toArray(Request $request): array
{
return [
'id' => $this->ulid,

View File

@ -4,6 +4,7 @@ namespace App\Http\Controllers\Api\V1;
use App\Domains\Api\Resources\LinkResource;
use App\Domains\Link\Actions\CreateLink;
use App\Domains\Link\Actions\DeleteLink;
use App\Domains\Link\Models\Link;
use App\Domains\Workspace\Models\Workspace;
use App\Http\Controllers\Controller;
@ -28,10 +29,11 @@ class LinkController extends Controller
$this->authorize('update', $workspace);
$validated = $request->validate([
'target_url' => 'required|url|max:2048',
'target_url' => 'required|url:http,https|max:2048',
'slug' => 'nullable|string|max:64|alpha_dash',
'title' => 'nullable|string|max:255',
'tags' => 'nullable|array',
'tags.*' => 'string|max:64',
]);
$link = $action->handle($workspace, $request->user(), $validated);
@ -41,7 +43,7 @@ class LinkController extends Controller
->setStatusCode(201);
}
public function destroy(string $workspaceUlid, string $linkUlid): \Illuminate\Http\Response
public function destroy(Request $request, string $workspaceUlid, string $linkUlid, DeleteLink $action): \Illuminate\Http\Response
{
$workspace = Workspace::where('ulid', $workspaceUlid)->firstOrFail();
$this->authorize('update', $workspace);
@ -50,7 +52,7 @@ class LinkController extends Controller
->where('workspace_id', $workspace->id)
->firstOrFail();
$link->delete();
$action->handle($link);
return response()->noContent();
}

View File

@ -12,8 +12,9 @@ class WorkspaceController extends Controller
{
$workspaces = $request->user()
->workspaces()
->get(['id', 'ulid', 'name', 'slug']);
->select(['ulid', 'name', 'slug'])
->paginate(50);
return response()->json(['data' => $workspaces]);
return response()->json($workspaces);
}
}

View File

@ -31,3 +31,34 @@ it('creates link via API', function () {
$response->assertCreated()
->assertJsonPath('data.target_url', 'https://example.com');
});
it('deletes a link via API', function () {
$user = User::factory()->create();
$workspace = (new \App\Domains\Workspace\Actions\CreateWorkspace)->handle($user, ['name' => 'Test']);
$link = Link::factory()->create(['workspace_id' => $workspace->id]);
$token = $user->createToken('api-test')->plainTextToken;
$response = $this->withToken($token)
->deleteJson("/api/v1/workspaces/{$workspace->ulid}/links/{$link->ulid}");
$response->assertNoContent();
$this->assertSoftDeleted($link);
});
it('rejects delete from non-member', function () {
$owner = User::factory()->create();
$outsider = User::factory()->create();
$workspace = (new \App\Domains\Workspace\Actions\CreateWorkspace)->handle($owner, ['name' => 'Test']);
$link = Link::factory()->create(['workspace_id' => $workspace->id]);
$token = $outsider->createToken('api-test')->plainTextToken;
$response = $this->withToken($token)
->deleteJson("/api/v1/workspaces/{$workspace->ulid}/links/{$link->ulid}");
$response->assertForbidden();
});
it('rejects unauthenticated requests', function () {
$response = $this->getJson('/api/v1/workspaces/ULID/links');
$response->assertUnauthorized();
});