Add notification filters
This commit is contained in:
@@ -4,6 +4,8 @@ namespace App\Actions\Notifications;
|
||||
|
||||
use App\Enums\NotificationChannel;
|
||||
use App\Enums\NotificationEventType;
|
||||
use App\Enums\NotificationFilterAttribute;
|
||||
use App\Enums\NotificationFilterOperator;
|
||||
use App\Http\Resources\NotificationActionResource;
|
||||
use App\Http\Resources\ProjectResource;
|
||||
use App\Models\Project;
|
||||
@@ -22,6 +24,8 @@ class BuildProjectNotificationSettingsPayload
|
||||
return [
|
||||
'channelOptions' => NotificationChannel::options(),
|
||||
'eventTypeOptions' => NotificationEventType::options(),
|
||||
'filterAttributeOptions' => NotificationFilterAttribute::options(),
|
||||
'filterOperatorOptions' => NotificationFilterOperator::options(),
|
||||
'notificationActions' => NotificationActionResource::collection(
|
||||
$this->listProjectNotificationActions->handle($project),
|
||||
)->resolve(),
|
||||
|
||||
@@ -0,0 +1,208 @@
|
||||
<?php
|
||||
|
||||
namespace App\Actions\Notifications;
|
||||
|
||||
use App\Enums\NotificationFilterOperator;
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
class MatchNotificationFilters
|
||||
{
|
||||
/**
|
||||
* @param array<int, array{attribute?: mixed, operator?: mixed, value?: mixed}> $filters
|
||||
*/
|
||||
public function handle(array $filters, TicketStatusChangedPayload $payload): bool
|
||||
{
|
||||
if ($filters === []) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$payloadContext = $this->payloadContext($payload);
|
||||
|
||||
foreach ($filters as $filter) {
|
||||
if (! $this->matchesFilter($filter, $payloadContext)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array{attribute?: mixed, operator?: mixed, value?: mixed} $filter
|
||||
* @param array<string, mixed> $payloadContext
|
||||
*/
|
||||
private function matchesFilter(array $filter, array $payloadContext): bool
|
||||
{
|
||||
$attribute = trim((string) Arr::get($filter, 'attribute', ''));
|
||||
|
||||
if ($attribute === '') {
|
||||
return false;
|
||||
}
|
||||
|
||||
$operator = NotificationFilterOperator::tryFrom((string) Arr::get($filter, 'operator', ''));
|
||||
|
||||
if ($operator === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$actualValue = data_get($payloadContext, $attribute);
|
||||
|
||||
return match ($operator) {
|
||||
NotificationFilterOperator::Equals => $this->matchesEquals($actualValue, Arr::get($filter, 'value')),
|
||||
NotificationFilterOperator::NotEquals => $this->matchesNotEquals($actualValue, Arr::get($filter, 'value')),
|
||||
NotificationFilterOperator::Exists => $actualValue !== null,
|
||||
NotificationFilterOperator::NotExists => $actualValue === null,
|
||||
NotificationFilterOperator::In => $this->matchesIn($actualValue, Arr::get($filter, 'value')),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, string>|string|null $expectedValue
|
||||
*/
|
||||
private function matchesEquals(mixed $actualValue, array|string|null $expectedValue): bool
|
||||
{
|
||||
if ($actualValue === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$normalizedExpectedValue = $this->normalizeComparable($expectedValue);
|
||||
|
||||
if ($normalizedExpectedValue === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->normalizeComparable($actualValue) === $normalizedExpectedValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, string>|string|null $expectedValue
|
||||
*/
|
||||
private function matchesNotEquals(mixed $actualValue, array|string|null $expectedValue): bool
|
||||
{
|
||||
$normalizedExpectedValue = $this->normalizeComparable($expectedValue);
|
||||
|
||||
if ($normalizedExpectedValue === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($actualValue === null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $this->normalizeComparable($actualValue) !== $normalizedExpectedValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, string>|string|null $expectedValues
|
||||
*/
|
||||
private function matchesIn(mixed $actualValue, array|string|null $expectedValues): bool
|
||||
{
|
||||
if ($actualValue === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$normalizedActualValue = $this->normalizeComparable($actualValue);
|
||||
|
||||
if ($normalizedActualValue === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$normalizedExpectedValues = $this->normalizeComparableList($expectedValues);
|
||||
|
||||
if ($normalizedExpectedValues === []) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return in_array($normalizedActualValue, $normalizedExpectedValues, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, string>
|
||||
*/
|
||||
private function normalizeComparableList(array|string|null $value): array
|
||||
{
|
||||
if (is_array($value)) {
|
||||
$normalized = [];
|
||||
|
||||
foreach ($value as $candidate) {
|
||||
$normalizedCandidate = $this->normalizeComparable($candidate);
|
||||
|
||||
if ($normalizedCandidate !== null) {
|
||||
$normalized[] = $normalizedCandidate;
|
||||
}
|
||||
}
|
||||
|
||||
return array_values(array_unique($normalized));
|
||||
}
|
||||
|
||||
if (! is_string($value)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$chunks = preg_split('/[,\r\n]+/', $value) ?: [];
|
||||
$normalized = [];
|
||||
|
||||
foreach ($chunks as $chunk) {
|
||||
$normalizedChunk = $this->normalizeComparable($chunk);
|
||||
|
||||
if ($normalizedChunk !== null) {
|
||||
$normalized[] = $normalizedChunk;
|
||||
}
|
||||
}
|
||||
|
||||
return array_values(array_unique($normalized));
|
||||
}
|
||||
|
||||
private function normalizeComparable(mixed $value): ?string
|
||||
{
|
||||
if (is_bool($value)) {
|
||||
return $value ? 'true' : 'false';
|
||||
}
|
||||
|
||||
if (is_int($value) || is_float($value)) {
|
||||
return trim((string) $value);
|
||||
}
|
||||
|
||||
if (! is_string($value)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$trimmedValue = trim($value);
|
||||
|
||||
return $trimmedValue === '' ? null : $trimmedValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function payloadContext(TicketStatusChangedPayload $payload): array
|
||||
{
|
||||
return [
|
||||
'project' => [
|
||||
'id' => $payload->projectId,
|
||||
'key' => $payload->projectKey,
|
||||
'name' => $payload->projectName,
|
||||
],
|
||||
'ticket' => [
|
||||
'id' => $payload->ticketId,
|
||||
'key' => $payload->ticketKey,
|
||||
'title' => $payload->ticketTitle,
|
||||
'url' => $payload->ticketUrl,
|
||||
],
|
||||
'actor' => [
|
||||
'id' => $payload->actorId,
|
||||
'name' => $payload->actorName,
|
||||
],
|
||||
'status' => [
|
||||
'from' => [
|
||||
'id' => $payload->fromStatusId,
|
||||
'label' => $payload->fromStatusLabel,
|
||||
],
|
||||
'to' => [
|
||||
'id' => $payload->toStatusId,
|
||||
'label' => $payload->toStatusLabel,
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
|
||||
namespace App\Actions\Notifications;
|
||||
|
||||
use App\Enums\NotificationFilterOperator;
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
class NormalizeNotificationFilters
|
||||
{
|
||||
/**
|
||||
* @return array<int, array{attribute: string, operator: string, value: array<int, string>|string|null}>
|
||||
*/
|
||||
public function handle(mixed $filters): array
|
||||
{
|
||||
if (! is_array($filters)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$normalizedFilters = [];
|
||||
|
||||
foreach ($filters as $filter) {
|
||||
if (! is_array($filter)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$attribute = trim((string) Arr::get($filter, 'attribute', ''));
|
||||
$operator = NotificationFilterOperator::tryFrom((string) Arr::get($filter, 'operator', ''));
|
||||
|
||||
if ($attribute === '' || $operator === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$normalizedFilters[] = [
|
||||
'attribute' => $attribute,
|
||||
'operator' => $operator->value,
|
||||
'value' => $this->normalizeValue($operator, Arr::get($filter, 'value')),
|
||||
];
|
||||
}
|
||||
|
||||
return $normalizedFilters;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, string>|string|null
|
||||
*/
|
||||
private function normalizeValue(NotificationFilterOperator $operator, mixed $value): array|string|null
|
||||
{
|
||||
return match ($operator) {
|
||||
NotificationFilterOperator::Equals,
|
||||
NotificationFilterOperator::NotEquals => $this->normalizeScalarValue($value),
|
||||
NotificationFilterOperator::Exists,
|
||||
NotificationFilterOperator::NotExists => null,
|
||||
NotificationFilterOperator::In => $this->normalizeListValue($value),
|
||||
};
|
||||
}
|
||||
|
||||
private function normalizeScalarValue(mixed $value): ?string
|
||||
{
|
||||
if (is_bool($value)) {
|
||||
return $value ? 'true' : 'false';
|
||||
}
|
||||
|
||||
if (is_int($value) || is_float($value)) {
|
||||
return trim((string) $value);
|
||||
}
|
||||
|
||||
if (! is_string($value)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$trimmedValue = trim($value);
|
||||
|
||||
return $trimmedValue === '' ? null : $trimmedValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, string>
|
||||
*/
|
||||
private function normalizeListValue(mixed $value): array
|
||||
{
|
||||
$normalizedValues = [];
|
||||
|
||||
if (is_string($value)) {
|
||||
$chunks = preg_split('/[,\r\n]+/', $value) ?: [];
|
||||
|
||||
foreach ($chunks as $chunk) {
|
||||
$trimmedValue = trim($chunk);
|
||||
|
||||
if ($trimmedValue !== '') {
|
||||
$normalizedValues[] = $trimmedValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (is_array($value)) {
|
||||
foreach ($value as $candidate) {
|
||||
$normalizedCandidate = $this->normalizeScalarValue($candidate);
|
||||
|
||||
if ($normalizedCandidate !== null) {
|
||||
$normalizedValues[] = $normalizedCandidate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return array_values(array_unique($normalizedValues));
|
||||
}
|
||||
}
|
||||
@@ -9,14 +9,22 @@ use App\Models\NotificationDeliveryLog;
|
||||
|
||||
class QueueTicketStatusChangedNotificationDeliveries
|
||||
{
|
||||
public function __construct(
|
||||
private readonly MatchNotificationFilters $matchNotificationFilters,
|
||||
) {}
|
||||
|
||||
public function handle(TicketStatusChanged|TicketCreated $event): void
|
||||
{
|
||||
NotificationAction::query()
|
||||
->enabled()
|
||||
->where('project_id', $event->payload->projectId)
|
||||
->where('event_type', $event->payload->eventType->value)
|
||||
->get(['channel', 'id'])
|
||||
->get(['channel', 'filters', 'id'])
|
||||
->each(function (NotificationAction $notificationAction) use ($event): void {
|
||||
if (! $this->matchNotificationFilters->handle($notificationAction->filters ?? [], $event->payload)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$notificationDeliveryLogId = null;
|
||||
|
||||
if (in_array($notificationAction->channel, [NotificationChannel::Discord, NotificationChannel::Webhook], true)) {
|
||||
|
||||
@@ -8,6 +8,10 @@ use Illuminate\Support\Arr;
|
||||
|
||||
class StoreProjectNotificationAction
|
||||
{
|
||||
public function __construct(
|
||||
private readonly NormalizeNotificationFilters $normalizeNotificationFilters,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* @param array{
|
||||
* channel: mixed,
|
||||
@@ -15,6 +19,7 @@ class StoreProjectNotificationAction
|
||||
* delivery_url: string,
|
||||
* enabled: bool,
|
||||
* event_type: mixed,
|
||||
* filters?: mixed,
|
||||
* message_template?: string|null,
|
||||
* name: string
|
||||
* } $validated
|
||||
@@ -27,6 +32,7 @@ class StoreProjectNotificationAction
|
||||
'delivery_url' => trim((string) Arr::get($validated, 'delivery_url')),
|
||||
'enabled' => (bool) Arr::get($validated, 'enabled'),
|
||||
'event_type' => Arr::get($validated, 'event_type'),
|
||||
'filters' => $this->normalizeNotificationFilters->handle(Arr::get($validated, 'filters')),
|
||||
'message_template' => $this->normalizeMessageTemplate(Arr::get($validated, 'message_template')),
|
||||
'name' => trim((string) Arr::get($validated, 'name')),
|
||||
]);
|
||||
|
||||
@@ -8,6 +8,10 @@ use Illuminate\Support\Arr;
|
||||
|
||||
class UpdateProjectNotificationAction
|
||||
{
|
||||
public function __construct(
|
||||
private readonly NormalizeNotificationFilters $normalizeNotificationFilters,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* @param array{
|
||||
* channel?: mixed,
|
||||
@@ -15,6 +19,7 @@ class UpdateProjectNotificationAction
|
||||
* delivery_url?: string|null,
|
||||
* enabled?: bool,
|
||||
* event_type?: mixed,
|
||||
* filters?: mixed,
|
||||
* message_template?: string|null,
|
||||
* name?: string
|
||||
* } $validated
|
||||
@@ -54,6 +59,12 @@ class UpdateProjectNotificationAction
|
||||
);
|
||||
}
|
||||
|
||||
if (array_key_exists('filters', $validated)) {
|
||||
$attributes['filters'] = $this->normalizeNotificationFilters->handle(
|
||||
Arr::get($validated, 'filters'),
|
||||
);
|
||||
}
|
||||
|
||||
$deliveryUrl = Arr::get($validated, 'delivery_url');
|
||||
|
||||
if (is_string($deliveryUrl) && trim($deliveryUrl) !== '') {
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
enum NotificationFilterAttribute: string
|
||||
{
|
||||
case ProjectId = 'project.id';
|
||||
case ProjectKey = 'project.key';
|
||||
case ProjectName = 'project.name';
|
||||
case TicketId = 'ticket.id';
|
||||
case TicketKey = 'ticket.key';
|
||||
case TicketTitle = 'ticket.title';
|
||||
case TicketUrl = 'ticket.url';
|
||||
case ActorId = 'actor.id';
|
||||
case ActorName = 'actor.name';
|
||||
case StatusFromId = 'status.from.id';
|
||||
case StatusFromLabel = 'status.from.label';
|
||||
case StatusToId = 'status.to.id';
|
||||
case StatusToLabel = 'status.to.label';
|
||||
|
||||
public function label(): string
|
||||
{
|
||||
return match ($this) {
|
||||
self::ProjectId => 'Project ID',
|
||||
self::ProjectKey => 'Project key',
|
||||
self::ProjectName => 'Project name',
|
||||
self::TicketId => 'Ticket ID',
|
||||
self::TicketKey => 'Ticket key',
|
||||
self::TicketTitle => 'Ticket title',
|
||||
self::TicketUrl => 'Ticket URL',
|
||||
self::ActorId => 'Actor ID',
|
||||
self::ActorName => 'Actor name',
|
||||
self::StatusFromId => 'From status ID',
|
||||
self::StatusFromLabel => 'From status label',
|
||||
self::StatusToId => 'To status ID',
|
||||
self::StatusToLabel => 'To status label',
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array{label: string, value: string}>
|
||||
*/
|
||||
public static function options(): array
|
||||
{
|
||||
return array_map(
|
||||
static fn (self $attribute): array => [
|
||||
'label' => $attribute->label(),
|
||||
'value' => $attribute->value,
|
||||
],
|
||||
self::cases(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public static function values(): array
|
||||
{
|
||||
return array_map(
|
||||
static fn (self $attribute): string => $attribute->value,
|
||||
self::cases(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
enum NotificationFilterOperator: string
|
||||
{
|
||||
case Equals = 'equals';
|
||||
case NotEquals = 'not_equals';
|
||||
case Exists = 'exists';
|
||||
case NotExists = 'not_exists';
|
||||
case In = 'in';
|
||||
|
||||
public function label(): string
|
||||
{
|
||||
return match ($this) {
|
||||
self::Equals => 'Equals',
|
||||
self::NotEquals => 'Not equals',
|
||||
self::Exists => 'Exists',
|
||||
self::NotExists => 'Not exists',
|
||||
self::In => 'In list',
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array{label: string, value: string}>
|
||||
*/
|
||||
public static function options(): array
|
||||
{
|
||||
return array_map(
|
||||
static fn (self $operator): array => [
|
||||
'label' => $operator->label(),
|
||||
'value' => $operator->value,
|
||||
],
|
||||
self::cases(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public static function values(): array
|
||||
{
|
||||
return array_map(
|
||||
static fn (self $operator): string => $operator->value,
|
||||
self::cases(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests\Concerns;
|
||||
|
||||
use App\Actions\Notifications\NormalizeNotificationFilters;
|
||||
use App\Enums\NotificationFilterAttribute;
|
||||
use App\Enums\NotificationFilterOperator;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Illuminate\Validation\Validator;
|
||||
|
||||
trait InteractsWithNotificationFilters
|
||||
{
|
||||
protected function prepareNotificationFiltersForValidation(): void
|
||||
{
|
||||
$rawFilters = $this->input('filters');
|
||||
|
||||
if (! is_string($rawFilters)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$decodedFilters = json_decode($rawFilters, true);
|
||||
|
||||
if (json_last_error() === JSON_ERROR_NONE) {
|
||||
$this->merge([
|
||||
'filters' => $decodedFilters,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
protected function notificationFilterRules(): array
|
||||
{
|
||||
return [
|
||||
'filters' => ['nullable', 'array', 'max:25'],
|
||||
'filters.*' => ['required', 'array'],
|
||||
'filters.*.attribute' => ['required', 'string', Rule::in(NotificationFilterAttribute::values())],
|
||||
'filters.*.operator' => ['required', 'string', Rule::in(NotificationFilterOperator::values())],
|
||||
'filters.*.value' => ['nullable'],
|
||||
];
|
||||
}
|
||||
|
||||
protected function withNotificationFilterValidator(Validator $validator): void
|
||||
{
|
||||
$validator->after(function (Validator $validator): void {
|
||||
$filters = $this->input('filters');
|
||||
|
||||
if (! is_array($filters)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$normalizer = app(NormalizeNotificationFilters::class);
|
||||
|
||||
foreach ($filters as $index => $filter) {
|
||||
if (! is_array($filter)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$operator = NotificationFilterOperator::tryFrom((string) ($filter['operator'] ?? null));
|
||||
|
||||
if ($operator === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$normalizedValue = $normalizer->handle([$filter])[0]['value'] ?? null;
|
||||
|
||||
if (in_array($operator, [NotificationFilterOperator::Equals, NotificationFilterOperator::NotEquals], true)
|
||||
&& ! is_string($normalizedValue)
|
||||
) {
|
||||
$validator->errors()->add(
|
||||
"filters.{$index}.value",
|
||||
'A non-empty value is required for equals and not equals operators.',
|
||||
);
|
||||
}
|
||||
|
||||
if ($operator === NotificationFilterOperator::In
|
||||
&& (! is_array($normalizedValue) || $normalizedValue === [])
|
||||
) {
|
||||
$validator->errors()->add(
|
||||
"filters.{$index}.value",
|
||||
'The in operator requires at least one value.',
|
||||
);
|
||||
}
|
||||
|
||||
if (in_array($operator, [NotificationFilterOperator::Exists, NotificationFilterOperator::NotExists], true)
|
||||
&& $this->hasProvidedFilterValue($filter['value'] ?? null)
|
||||
) {
|
||||
$validator->errors()->add(
|
||||
"filters.{$index}.value",
|
||||
'The exists and not exists operators do not accept a value.',
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private function hasProvidedFilterValue(mixed $value): bool
|
||||
{
|
||||
if ($value === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (is_string($value)) {
|
||||
return trim($value) !== '';
|
||||
}
|
||||
|
||||
if (is_array($value)) {
|
||||
foreach ($value as $candidate) {
|
||||
if (is_string($candidate) && trim($candidate) !== '') {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (! is_string($candidate) && $candidate !== null) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -4,12 +4,16 @@ namespace App\Http\Requests;
|
||||
|
||||
use App\Enums\NotificationChannel;
|
||||
use App\Enums\NotificationEventType;
|
||||
use App\Http\Requests\Concerns\InteractsWithNotificationFilters;
|
||||
use Illuminate\Contracts\Validation\ValidationRule;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Illuminate\Validation\Validator;
|
||||
|
||||
class StoreProjectNotificationActionRequest extends FormRequest
|
||||
{
|
||||
use InteractsWithNotificationFilters;
|
||||
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*/
|
||||
@@ -32,9 +36,20 @@ class StoreProjectNotificationActionRequest extends FormRequest
|
||||
'enabled' => ['required', 'boolean'],
|
||||
'delivery_url' => ['required', 'string', 'max:2048', 'url'],
|
||||
'message_template' => $this->messageTemplateRules(),
|
||||
...$this->notificationFilterRules(),
|
||||
];
|
||||
}
|
||||
|
||||
protected function prepareForValidation(): void
|
||||
{
|
||||
$this->prepareNotificationFiltersForValidation();
|
||||
}
|
||||
|
||||
public function withValidator(Validator $validator): void
|
||||
{
|
||||
$this->withNotificationFilterValidator($validator);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, string>
|
||||
*/
|
||||
|
||||
@@ -4,12 +4,16 @@ namespace App\Http\Requests;
|
||||
|
||||
use App\Enums\NotificationChannel;
|
||||
use App\Enums\NotificationEventType;
|
||||
use App\Http\Requests\Concerns\InteractsWithNotificationFilters;
|
||||
use Illuminate\Contracts\Validation\ValidationRule;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Illuminate\Validation\Validator;
|
||||
|
||||
class UpdateProjectNotificationActionRequest extends FormRequest
|
||||
{
|
||||
use InteractsWithNotificationFilters;
|
||||
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*/
|
||||
@@ -32,9 +36,20 @@ class UpdateProjectNotificationActionRequest extends FormRequest
|
||||
'enabled' => ['required', 'boolean'],
|
||||
'delivery_url' => ['nullable', 'string', 'max:2048', 'url'],
|
||||
'message_template' => $this->messageTemplateRules(),
|
||||
...$this->notificationFilterRules(),
|
||||
];
|
||||
}
|
||||
|
||||
protected function prepareForValidation(): void
|
||||
{
|
||||
$this->prepareNotificationFiltersForValidation();
|
||||
}
|
||||
|
||||
public function withValidator(Validator $validator): void
|
||||
{
|
||||
$this->withNotificationFilterValidator($validator);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, string>
|
||||
*/
|
||||
|
||||
@@ -25,6 +25,7 @@ class NotificationActionResource extends JsonResource
|
||||
'channel' => $this->channel->value,
|
||||
'channel_label' => $this->channel->label(),
|
||||
'channel_configuration' => $this->channel_configuration ?? [],
|
||||
'filters' => $this->filters ?? [],
|
||||
'has_delivery_url' => $this->resource instanceof NotificationAction && filled($this->delivery_url),
|
||||
'message_template' => $this->message_template,
|
||||
'created_at' => $this->created_at?->toISOString(),
|
||||
|
||||
@@ -19,6 +19,7 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
'delivery_url',
|
||||
'enabled',
|
||||
'event_type',
|
||||
'filters',
|
||||
'message_template',
|
||||
'name',
|
||||
'project_id',
|
||||
@@ -40,6 +41,7 @@ class NotificationAction extends Model
|
||||
'delivery_url' => 'encrypted',
|
||||
'enabled' => 'boolean',
|
||||
'event_type' => NotificationEventType::class,
|
||||
'filters' => 'array',
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user