Merge branch 'main' into codex/v0.5-webhook-notification/06-notification-tests-and-release-hardening
linter / quality (pull_request) Successful in 1m22s
tests / ci (8.4) (pull_request) Successful in 1m46s

# Conflicts:
#	tests/Feature/Mcp/KanbanGitMcpTest.php
This commit is contained in:
2026-04-24 18:51:56 +02:00
16 changed files with 229 additions and 8 deletions
@@ -279,6 +279,9 @@ class DeliverNotificationAction implements ShouldQueue
->timeout(10)
->post($notificationAction->delivery_url, [
'content' => $message,
'allowed_mentions' => [
'parse' => [],
],
]);
}
@@ -59,6 +59,7 @@ class BuildProjectBacklogPayload
)->resolve(),
'users' => UserResource::collection(
User::query()
->visibleToProject($project)
->orderBy('name')
->get(),
)->resolve(),
@@ -116,6 +116,7 @@ class BuildProjectTicketDetailPayload
'ticketTypes' => TicketTypeResource::collection($project->ticketTypes()->get())->resolve(),
'users' => UserResource::collection(
User::query()
->visibleToProject($project)
->orderBy('name')
->get(),
)->resolve(),
@@ -38,6 +38,7 @@ class BuildProjectTicketOptionsPayload
'ticketTypes' => TicketTypeResource::collection($project->ticketTypes()->get())->resolve(),
'users' => UserResource::collection(
User::query()
->visibleToProject($project)
->orderBy('name')
->get(),
)->resolve(),
@@ -123,7 +123,8 @@ class ProjectTicketController extends ApiController
? "{$ticket->ticket_key} was updated."
: 'No ticket changes were detected.';
return TicketResource::make($ticket->fresh()->load([
$canManageProject = $this->permissionService->allows($user, Permission::ProjectAdmin, $project);
$displayTicket = $ticket->fresh([
'assignee',
'author',
'fieldValues.ticketFieldDefinition',
@@ -132,7 +133,23 @@ class ProjectTicketController extends ApiController
'ticketStatus.boardColumn',
'ticketType',
'worklogEntries.actor',
]))
]) ?? $ticket->load([
'assignee',
'author',
'fieldValues.ticketFieldDefinition',
'fieldValues.userValue',
'ticketPriority',
'ticketStatus.boardColumn',
'ticketType',
'worklogEntries.actor',
]);
if (! $project->show_author && ! $canManageProject) {
$displayTicket->setRelation('author', null);
$displayTicket->author_user_id = null;
}
return TicketResource::make($displayTicket)
->additional([
'meta' => [
'message' => $statusMessage,
@@ -18,10 +18,14 @@ class UserAssignedTicketController extends ApiController
{
$viewer = $this->authenticatedUser($request);
$assignedTickets = $this->listAssignedTicketsByVisibleProject->handle($viewer, $user);
return response()->json([
'data' => $this->listAssignedTicketsByVisibleProject->handle($viewer, $user),
'data' => $assignedTickets,
'meta' => [
'assignee' => UserResource::make($user)->resolve(),
'assignee' => $assignedTickets === []
? null
: UserResource::make($user)->resolve(),
],
]);
}
+3 -1
View File
@@ -31,7 +31,9 @@ class ListTicketsByStatus extends Tool
'project_id' => ['nullable', 'integer'],
'status_name' => ['nullable', 'string'],
'status_slug' => ['nullable', 'string'],
'ticket_status_id' => ['nullable', 'integer'],
'ticket_status_id' => ['nullable', 'integer', 'required_without_all:status_slug,status_name'],
], [
'ticket_status_id.required_without_all' => 'Provide ticket_status_id, status_slug, or status_name.',
]);
$user = $this->kanbanGitMcp->user($request);
+21
View File
@@ -3,9 +3,12 @@
namespace App\Models;
// use Illuminate\Contracts\Auth\MustVerifyEmail;
use App\Actions\Permissions\PermissionService;
use App\Enums\Permission;
use Database\Factories\UserFactory;
use Illuminate\Database\Eloquent\Attributes\Fillable;
use Illuminate\Database\Eloquent\Attributes\Hidden;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
@@ -76,4 +79,22 @@ class User extends Authenticatable
{
return $this->hasMany(Ticket::class, 'author_user_id');
}
public function scopeVisibleToProject(Builder $query, Project $project): Builder
{
$permissionService = app(PermissionService::class);
$allowedUserIds = User::query()
->select('id')
->cursor()
->filter(fn (User $candidate) => $permissionService->allows(
$candidate,
Permission::ProjectRead,
$project
))
->pluck('id')
->all();
return $query->whereKey($allowedUserIds);
}
}