mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-09-30 02:26:58 +00:00
Expand webhooks to support multiple delivery payloads, event triggers and responses.
This commit is contained in:
@@ -32,7 +32,9 @@ use FireflyIII\Generator\Webhook\MessageGeneratorInterface;
|
|||||||
use FireflyIII\Models\TransactionGroup;
|
use FireflyIII\Models\TransactionGroup;
|
||||||
use FireflyIII\Models\Webhook;
|
use FireflyIII\Models\Webhook;
|
||||||
use FireflyIII\Repositories\Webhook\WebhookRepositoryInterface;
|
use FireflyIII\Repositories\Webhook\WebhookRepositoryInterface;
|
||||||
|
use FireflyIII\Support\JsonApi\Enrichments\WebhookEnrichment;
|
||||||
use FireflyIII\Transformers\WebhookTransformer;
|
use FireflyIII\Transformers\WebhookTransformer;
|
||||||
|
use FireflyIII\User;
|
||||||
use Illuminate\Http\JsonResponse;
|
use Illuminate\Http\JsonResponse;
|
||||||
use Illuminate\Pagination\LengthAwarePaginator;
|
use Illuminate\Pagination\LengthAwarePaginator;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
@@ -90,6 +92,13 @@ class ShowController extends Controller
|
|||||||
$paginator = new LengthAwarePaginator($webhooks, $count, $pageSize, $this->parameters->get('page'));
|
$paginator = new LengthAwarePaginator($webhooks, $count, $pageSize, $this->parameters->get('page'));
|
||||||
$paginator->setPath(route('api.v1.webhooks.index').$this->buildParams());
|
$paginator->setPath(route('api.v1.webhooks.index').$this->buildParams());
|
||||||
|
|
||||||
|
// enrich
|
||||||
|
/** @var User $admin */
|
||||||
|
$admin = auth()->user();
|
||||||
|
$enrichment = new WebhookEnrichment();
|
||||||
|
$enrichment->setUser($admin);
|
||||||
|
$webhooks = $enrichment->enrich($webhooks);
|
||||||
|
|
||||||
/** @var WebhookTransformer $transformer */
|
/** @var WebhookTransformer $transformer */
|
||||||
$transformer = app(WebhookTransformer::class);
|
$transformer = app(WebhookTransformer::class);
|
||||||
$transformer->setParameters($this->parameters);
|
$transformer->setParameters($this->parameters);
|
||||||
|
@@ -75,6 +75,7 @@ class UpgradesDatabase extends Command
|
|||||||
'upgrade:610-currency-preferences',
|
'upgrade:610-currency-preferences',
|
||||||
'upgrade:620-piggy-banks',
|
'upgrade:620-piggy-banks',
|
||||||
'upgrade:620-pc-amounts',
|
'upgrade:620-pc-amounts',
|
||||||
|
'upgrade:640-upgrade-webhooks',
|
||||||
'firefly-iii:correct-database',
|
'firefly-iii:correct-database',
|
||||||
];
|
];
|
||||||
$args = [];
|
$args = [];
|
||||||
|
90
app/Console/Commands/Upgrade/UpgradesWebhooks.php
Normal file
90
app/Console/Commands/Upgrade/UpgradesWebhooks.php
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace FireflyIII\Console\Commands\Upgrade;
|
||||||
|
|
||||||
|
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
||||||
|
use FireflyIII\Enums\WebhookDelivery;
|
||||||
|
use FireflyIII\Enums\WebhookResponse;
|
||||||
|
use FireflyIII\Enums\WebhookTrigger;
|
||||||
|
use FireflyIII\Models\Webhook;
|
||||||
|
use FireflyIII\Models\WebhookDelivery as WebhookDeliveryModel;
|
||||||
|
use FireflyIII\Models\WebhookResponse as WebhookResponseModel;
|
||||||
|
use FireflyIII\Models\WebhookTrigger as WebhookTriggerModel;
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
|
||||||
|
class UpgradesWebhooks extends Command
|
||||||
|
{
|
||||||
|
use ShowsFriendlyMessages;
|
||||||
|
|
||||||
|
public const string CONFIG_NAME = '640_upgrade_webhooks';
|
||||||
|
protected $description = 'Upgrade webhooks so they can handle multiple triggers.';
|
||||||
|
protected $signature = 'upgrade:640-upgrade-webhooks {--F|force : Force the execution of this command.}';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*/
|
||||||
|
public function handle(): int
|
||||||
|
{
|
||||||
|
if ($this->isExecuted() && true !== $this->option('force')) {
|
||||||
|
$this->friendlyInfo('This command has already been executed.');
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->upgradeWebhooks();
|
||||||
|
$this->markAsExecuted();
|
||||||
|
$this->friendlyPositive('Upgraded webhooks.');
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function isExecuted(): bool
|
||||||
|
{
|
||||||
|
$configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false);
|
||||||
|
if (null !== $configVar) {
|
||||||
|
return (bool)$configVar->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private function upgradeWebhooks(): void
|
||||||
|
{
|
||||||
|
$set = Webhook::where('delivery', '>', 1)->orWhere('trigger', '>', 1)->orWhere('response', '>', 1)->get();
|
||||||
|
/** @var Webhook $webhook */
|
||||||
|
foreach ($set as $webhook) {
|
||||||
|
$this->upgradeWebhook($webhook);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function upgradeWebhook(Webhook $webhook): void
|
||||||
|
{
|
||||||
|
$delivery = WebhookDelivery::tryFrom((int)$webhook->delivery);
|
||||||
|
$response = WebhookResponse::tryFrom((int)$webhook->response);
|
||||||
|
$trigger = WebhookTrigger::tryFrom((int)$webhook->trigger);
|
||||||
|
if (null === $delivery || null === $response || null === $trigger) {
|
||||||
|
$this->friendlyError(sprintf('[a] Webhook #%d has an invalid delivery, response or trigger value. Will not upgrade.', $webhook->id));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$deliveryModel = WebhookDeliveryModel::where('key', $delivery->value)->first();
|
||||||
|
$responseModel = WebhookResponseModel::where('key', $response->value)->first();
|
||||||
|
$triggerModel = WebhookTriggerModel::where('key', $trigger->value)->first();
|
||||||
|
if (null === $deliveryModel || null === $responseModel || null === $triggerModel) {
|
||||||
|
$this->friendlyError(sprintf('[b] Webhook #%d has an invalid delivery, response or trigger model. Will not upgrade.', $webhook->id));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$webhook->webhookDeliveries()->attach([$deliveryModel->id]);
|
||||||
|
$webhook->webhookResponses()->attach([$responseModel->id]);
|
||||||
|
$webhook->webhookTriggers()->attach([$triggerModel->id]);
|
||||||
|
$webhook->delivery = 1;
|
||||||
|
$webhook->response = 1;
|
||||||
|
$webhook->trigger = 1;
|
||||||
|
$webhook->save();
|
||||||
|
$this->friendlyPositive(sprintf('Webhook #%d upgraded.', $webhook->id));
|
||||||
|
}
|
||||||
|
private function markAsExecuted(): void
|
||||||
|
{
|
||||||
|
app('fireflyconfig')->set(self::CONFIG_NAME, true);
|
||||||
|
}
|
||||||
|
}
|
@@ -31,6 +31,10 @@ use FireflyIII\Events\RequestedVersionCheckStatus;
|
|||||||
use FireflyIII\Exceptions\FireflyException;
|
use FireflyIII\Exceptions\FireflyException;
|
||||||
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
|
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
|
||||||
use FireflyIII\Http\Middleware\Installer;
|
use FireflyIII\Http\Middleware\Installer;
|
||||||
|
use FireflyIII\Models\Webhook;
|
||||||
|
use FireflyIII\Models\WebhookDelivery;
|
||||||
|
use FireflyIII\Models\WebhookResponse;
|
||||||
|
use FireflyIII\Models\WebhookTrigger;
|
||||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||||
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
|
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
|
||||||
use FireflyIII\User;
|
use FireflyIII\User;
|
||||||
@@ -121,6 +125,27 @@ class HomeController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function index(AccountRepositoryInterface $repository): mixed
|
public function index(AccountRepositoryInterface $repository): mixed
|
||||||
{
|
{
|
||||||
|
$trigger = WebhookTrigger::first();
|
||||||
|
$delivery = WebhookDelivery::first();
|
||||||
|
$response = WebhookResponse::first();
|
||||||
|
|
||||||
|
$webhook = new Webhook();
|
||||||
|
$webhook->user_id = auth()->id();
|
||||||
|
$webhook->user_group_id = auth()->user()->user_group_id;
|
||||||
|
$webhook->title = 'Test Webhook';
|
||||||
|
$webhook->url = 'https://example.com/webhook';
|
||||||
|
$webhook->active = true;
|
||||||
|
$webhook->secret = 'abc';
|
||||||
|
$webhook->trigger = 1;
|
||||||
|
$webhook->response = 1;
|
||||||
|
$webhook->delivery = 1;
|
||||||
|
$webhook->save();
|
||||||
|
$webhook->webhookDeliveries()->saveMany(new Collection()->push($delivery));
|
||||||
|
$webhook->webhookResponses()->saveMany(new Collection()->push($response));
|
||||||
|
$webhook->webhookTriggers()->saveMany(new Collection()->push($trigger));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
$types = config('firefly.accountTypesByIdentifier.asset');
|
$types = config('firefly.accountTypesByIdentifier.asset');
|
||||||
$count = $repository->count($types);
|
$count = $repository->count($types);
|
||||||
Log::channel('audit')->info('User visits homepage.');
|
Log::channel('audit')->info('User visits homepage.');
|
||||||
|
@@ -24,14 +24,15 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace FireflyIII\Models;
|
namespace FireflyIII\Models;
|
||||||
|
|
||||||
use FireflyIII\Enums\WebhookDelivery;
|
use FireflyIII\Enums\WebhookDelivery as WebhookDeliveryEnum;
|
||||||
use FireflyIII\Enums\WebhookResponse;
|
use FireflyIII\Enums\WebhookResponse as WebhookResponseEnum;
|
||||||
use FireflyIII\Enums\WebhookTrigger;
|
use FireflyIII\Enums\WebhookTrigger as WebhookTriggerEnum;
|
||||||
use FireflyIII\Support\Models\ReturnsIntegerIdTrait;
|
use FireflyIII\Support\Models\ReturnsIntegerIdTrait;
|
||||||
use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait;
|
use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait;
|
||||||
use FireflyIII\User;
|
use FireflyIII\User;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||||
@@ -56,7 +57,7 @@ class Webhook extends Model
|
|||||||
public static function getDeliveries(): array
|
public static function getDeliveries(): array
|
||||||
{
|
{
|
||||||
$array = [];
|
$array = [];
|
||||||
$set = WebhookDelivery::cases();
|
$set = WebhookDeliveryEnum::cases();
|
||||||
foreach ($set as $item) {
|
foreach ($set as $item) {
|
||||||
$array[$item->value] = $item->name;
|
$array[$item->value] = $item->name;
|
||||||
}
|
}
|
||||||
@@ -67,7 +68,7 @@ class Webhook extends Model
|
|||||||
public static function getDeliveriesForValidation(): array
|
public static function getDeliveriesForValidation(): array
|
||||||
{
|
{
|
||||||
$array = [];
|
$array = [];
|
||||||
$set = WebhookDelivery::cases();
|
$set = WebhookDeliveryEnum::cases();
|
||||||
foreach ($set as $item) {
|
foreach ($set as $item) {
|
||||||
$array[$item->name] = $item->value;
|
$array[$item->name] = $item->value;
|
||||||
$array[$item->value] = $item->value;
|
$array[$item->value] = $item->value;
|
||||||
@@ -79,7 +80,7 @@ class Webhook extends Model
|
|||||||
public static function getResponses(): array
|
public static function getResponses(): array
|
||||||
{
|
{
|
||||||
$array = [];
|
$array = [];
|
||||||
$set = WebhookResponse::cases();
|
$set = WebhookResponseEnum::cases();
|
||||||
foreach ($set as $item) {
|
foreach ($set as $item) {
|
||||||
$array[$item->value] = $item->name;
|
$array[$item->value] = $item->name;
|
||||||
}
|
}
|
||||||
@@ -90,7 +91,7 @@ class Webhook extends Model
|
|||||||
public static function getResponsesForValidation(): array
|
public static function getResponsesForValidation(): array
|
||||||
{
|
{
|
||||||
$array = [];
|
$array = [];
|
||||||
$set = WebhookResponse::cases();
|
$set = WebhookResponseEnum::cases();
|
||||||
foreach ($set as $item) {
|
foreach ($set as $item) {
|
||||||
$array[$item->name] = $item->value;
|
$array[$item->name] = $item->value;
|
||||||
$array[$item->value] = $item->value;
|
$array[$item->value] = $item->value;
|
||||||
@@ -102,7 +103,7 @@ class Webhook extends Model
|
|||||||
public static function getTriggers(): array
|
public static function getTriggers(): array
|
||||||
{
|
{
|
||||||
$array = [];
|
$array = [];
|
||||||
$set = WebhookTrigger::cases();
|
$set = WebhookTriggerEnum::cases();
|
||||||
foreach ($set as $item) {
|
foreach ($set as $item) {
|
||||||
$array[$item->value] = $item->name;
|
$array[$item->value] = $item->name;
|
||||||
}
|
}
|
||||||
@@ -113,7 +114,7 @@ class Webhook extends Model
|
|||||||
public static function getTriggersForValidation(): array
|
public static function getTriggersForValidation(): array
|
||||||
{
|
{
|
||||||
$array = [];
|
$array = [];
|
||||||
$set = WebhookTrigger::cases();
|
$set = WebhookTriggerEnum::cases();
|
||||||
foreach ($set as $item) {
|
foreach ($set as $item) {
|
||||||
$array[$item->name] = $item->value;
|
$array[$item->name] = $item->value;
|
||||||
$array[$item->value] = $item->value;
|
$array[$item->value] = $item->value;
|
||||||
@@ -130,13 +131,13 @@ class Webhook extends Model
|
|||||||
public static function routeBinder(string $value): self
|
public static function routeBinder(string $value): self
|
||||||
{
|
{
|
||||||
if (auth()->check()) {
|
if (auth()->check()) {
|
||||||
$webhookId = (int) $value;
|
$webhookId = (int)$value;
|
||||||
|
|
||||||
/** @var User $user */
|
/** @var User $user */
|
||||||
$user = auth()->user();
|
$user = auth()->user();
|
||||||
|
|
||||||
/** @var null|Webhook $webhook */
|
/** @var null|Webhook $webhook */
|
||||||
$webhook = $user->webhooks()->find($webhookId);
|
$webhook = $user->webhooks()->find($webhookId);
|
||||||
if (null !== $webhook) {
|
if (null !== $webhook) {
|
||||||
return $webhook;
|
return $webhook;
|
||||||
}
|
}
|
||||||
@@ -155,6 +156,21 @@ class Webhook extends Model
|
|||||||
return $this->hasMany(WebhookMessage::class);
|
return $this->hasMany(WebhookMessage::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function webhookDeliveries(): BelongsToMany
|
||||||
|
{
|
||||||
|
return $this->belongsToMany(WebhookDelivery::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function webhookResponses(): BelongsToMany
|
||||||
|
{
|
||||||
|
return $this->belongsToMany(WebhookResponse::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function webhookTriggers(): BelongsToMany
|
||||||
|
{
|
||||||
|
return $this->belongsToMany(WebhookTrigger::class);
|
||||||
|
}
|
||||||
|
|
||||||
protected function casts(): array
|
protected function casts(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
|
26
app/Models/WebhookDelivery.php
Normal file
26
app/Models/WebhookDelivery.php
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace FireflyIII\Models;
|
||||||
|
|
||||||
|
use FireflyIII\Support\Models\ReturnsIntegerIdTrait;
|
||||||
|
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
class WebhookDelivery extends Model
|
||||||
|
{
|
||||||
|
use ReturnsIntegerIdTrait;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the ID
|
||||||
|
*
|
||||||
|
* @SuppressWarnings("PHPMD.ShortMethodName")
|
||||||
|
*/
|
||||||
|
protected function key(): Attribute
|
||||||
|
{
|
||||||
|
return Attribute::make(
|
||||||
|
get: static fn ($value) => (int) $value,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
26
app/Models/WebhookResponse.php
Normal file
26
app/Models/WebhookResponse.php
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace FireflyIII\Models;
|
||||||
|
|
||||||
|
use FireflyIII\Support\Models\ReturnsIntegerIdTrait;
|
||||||
|
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
class WebhookResponse extends Model
|
||||||
|
{
|
||||||
|
use ReturnsIntegerIdTrait;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the ID
|
||||||
|
*
|
||||||
|
* @SuppressWarnings("PHPMD.ShortMethodName")
|
||||||
|
*/
|
||||||
|
protected function key(): Attribute
|
||||||
|
{
|
||||||
|
return Attribute::make(
|
||||||
|
get: static fn ($value) => (int) $value,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
26
app/Models/WebhookTrigger.php
Normal file
26
app/Models/WebhookTrigger.php
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace FireflyIII\Models;
|
||||||
|
|
||||||
|
use FireflyIII\Support\Models\ReturnsIntegerIdTrait;
|
||||||
|
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
class WebhookTrigger extends Model
|
||||||
|
{
|
||||||
|
use ReturnsIntegerIdTrait;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the ID
|
||||||
|
*
|
||||||
|
* @SuppressWarnings("PHPMD.ShortMethodName")
|
||||||
|
*/
|
||||||
|
protected function key(): Attribute
|
||||||
|
{
|
||||||
|
return Attribute::make(
|
||||||
|
get: static fn ($value) => (int) $value,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@@ -41,7 +41,12 @@ class WebhookRepository implements WebhookRepositoryInterface, UserGroupInterfac
|
|||||||
|
|
||||||
public function all(): Collection
|
public function all(): Collection
|
||||||
{
|
{
|
||||||
return $this->user->webhooks()->get();
|
return $this->user->webhooks()
|
||||||
|
// only get upgraded webhooks
|
||||||
|
->where('delivery', 1)
|
||||||
|
->where('response', 1)
|
||||||
|
->where('trigger', 1)
|
||||||
|
->get();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function destroy(Webhook $webhook): void
|
public function destroy(Webhook $webhook): void
|
||||||
@@ -67,22 +72,20 @@ class WebhookRepository implements WebhookRepositoryInterface, UserGroupInterfac
|
|||||||
public function getMessages(Webhook $webhook): Collection
|
public function getMessages(Webhook $webhook): Collection
|
||||||
{
|
{
|
||||||
return $webhook->webhookMessages()
|
return $webhook->webhookMessages()
|
||||||
->orderBy('created_at', 'DESC')
|
->orderBy('created_at', 'DESC')
|
||||||
->get(['webhook_messages.*'])
|
->get(['webhook_messages.*']);
|
||||||
;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getReadyMessages(Webhook $webhook): Collection
|
public function getReadyMessages(Webhook $webhook): Collection
|
||||||
{
|
{
|
||||||
return $webhook->webhookMessages()
|
return $webhook->webhookMessages()
|
||||||
->where('webhook_messages.sent', 0)
|
->where('webhook_messages.sent', 0)
|
||||||
->where('webhook_messages.errored', 0)
|
->where('webhook_messages.errored', 0)
|
||||||
->get(['webhook_messages.*'])
|
->get(['webhook_messages.*'])
|
||||||
->filter(
|
->filter(
|
||||||
static fn (WebhookMessage $message) // @phpstan-ignore-line
|
static fn(WebhookMessage $message) // @phpstan-ignore-line
|
||||||
=> $message->webhookAttempts()->count() <= 2
|
=> $message->webhookAttempts()->count() <= 2
|
||||||
)->splice(0, 3)
|
)->splice(0, 3);
|
||||||
;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function store(array $data): Webhook
|
public function store(array $data): Webhook
|
||||||
@@ -93,9 +96,12 @@ class WebhookRepository implements WebhookRepositoryInterface, UserGroupInterfac
|
|||||||
'user_group_id' => $this->user->user_group_id,
|
'user_group_id' => $this->user->user_group_id,
|
||||||
'active' => $data['active'] ?? false,
|
'active' => $data['active'] ?? false,
|
||||||
'title' => $data['title'] ?? null,
|
'title' => $data['title'] ?? null,
|
||||||
'trigger' => $data['trigger'],
|
// 'trigger' => $data['trigger'],
|
||||||
'response' => $data['response'],
|
// 'response' => $data['response'],
|
||||||
'delivery' => $data['delivery'],
|
// 'delivery' => $data['delivery'],
|
||||||
|
'trigger' => 1,
|
||||||
|
'response' => 1,
|
||||||
|
'delivery' => 1,
|
||||||
'secret' => $secret,
|
'secret' => $secret,
|
||||||
'url' => $data['url'],
|
'url' => $data['url'],
|
||||||
];
|
];
|
||||||
@@ -105,12 +111,12 @@ class WebhookRepository implements WebhookRepositoryInterface, UserGroupInterfac
|
|||||||
|
|
||||||
public function update(Webhook $webhook, array $data): Webhook
|
public function update(Webhook $webhook, array $data): Webhook
|
||||||
{
|
{
|
||||||
$webhook->active = $data['active'] ?? $webhook->active;
|
$webhook->active = $data['active'] ?? $webhook->active;
|
||||||
$webhook->trigger = $data['trigger'] ?? $webhook->trigger;
|
// $webhook->trigger = $data['trigger'] ?? $webhook->trigger;
|
||||||
$webhook->response = $data['response'] ?? $webhook->response;
|
// $webhook->response = $data['response'] ?? $webhook->response;
|
||||||
$webhook->delivery = $data['delivery'] ?? $webhook->delivery;
|
// $webhook->delivery = $data['delivery'] ?? $webhook->delivery;
|
||||||
$webhook->title = $data['title'] ?? $webhook->title;
|
$webhook->title = $data['title'] ?? $webhook->title;
|
||||||
$webhook->url = $data['url'] ?? $webhook->url;
|
$webhook->url = $data['url'] ?? $webhook->url;
|
||||||
|
|
||||||
if (true === $data['secret']) {
|
if (true === $data['secret']) {
|
||||||
$secret = Str::random(24);
|
$secret = Str::random(24);
|
||||||
|
134
app/Support/JsonApi/Enrichments/WebhookEnrichment.php
Normal file
134
app/Support/JsonApi/Enrichments/WebhookEnrichment.php
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace FireflyIII\Support\JsonApi\Enrichments;
|
||||||
|
|
||||||
|
use FireflyIII\Enums\WebhookDelivery as WebhookDeliveryEnum;
|
||||||
|
use FireflyIII\Enums\WebhookResponse as WebhookResponseEnum;
|
||||||
|
use FireflyIII\Enums\WebhookTrigger as WebhookTriggerEnum;
|
||||||
|
use FireflyIII\Models\UserGroup;
|
||||||
|
use FireflyIII\Models\Webhook;
|
||||||
|
use FireflyIII\Models\WebhookDelivery;
|
||||||
|
use FireflyIII\Models\WebhookResponse;
|
||||||
|
use FireflyIII\Models\WebhookTrigger;
|
||||||
|
use FireflyIII\User;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
class WebhookEnrichment implements EnrichmentInterface
|
||||||
|
{
|
||||||
|
private Collection $collection;
|
||||||
|
private User $user;
|
||||||
|
private UserGroup $userGroup;
|
||||||
|
private array $ids = [];
|
||||||
|
private array $deliveries = [];
|
||||||
|
private array $responses = [];
|
||||||
|
private array $triggers = [];
|
||||||
|
|
||||||
|
private array $webhookDeliveries = [];
|
||||||
|
private array $webhookResponses = [];
|
||||||
|
private array $webhookTriggers = [];
|
||||||
|
|
||||||
|
|
||||||
|
public function enrich(Collection $collection): Collection
|
||||||
|
{
|
||||||
|
$this->collection = $collection;
|
||||||
|
if ($this->collection->count() > 0) {
|
||||||
|
$this->collectIds();
|
||||||
|
$this->collectInfo();
|
||||||
|
$this->collectWebhookInfo();
|
||||||
|
$this->appendCollectedInfo();
|
||||||
|
}
|
||||||
|
return $this->collection;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function enrichSingle(Model|array $model): array|Model
|
||||||
|
{
|
||||||
|
Log::debug(__METHOD__);
|
||||||
|
$collection = new Collection([$model]);
|
||||||
|
$collection = $this->enrich($collection);
|
||||||
|
|
||||||
|
return $collection->first();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setUser(User $user): void
|
||||||
|
{
|
||||||
|
$this->user = $user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setUserGroup(UserGroup $userGroup): void
|
||||||
|
{
|
||||||
|
$this->userGroup = $userGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function collectIds(): void
|
||||||
|
{
|
||||||
|
/** @var Webhook $webhook */
|
||||||
|
foreach ($this->collection as $webhook) {
|
||||||
|
$this->ids[] = $webhook->id;
|
||||||
|
}
|
||||||
|
$this->ids = array_unique($this->ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function collectInfo(): void
|
||||||
|
{
|
||||||
|
$all = WebhookDelivery::get();
|
||||||
|
/** @var WebhookDelivery $item */
|
||||||
|
foreach ($all as $item) {
|
||||||
|
$this->deliveries[$item->id] = $item->key;
|
||||||
|
}
|
||||||
|
$all = WebhookResponse::get();
|
||||||
|
/** @var WebhookResponse $item */
|
||||||
|
foreach ($all as $item) {
|
||||||
|
$this->responses[$item->id] = $item->key;
|
||||||
|
}
|
||||||
|
$all = WebhookTrigger::get();
|
||||||
|
/** @var WebhookTrigger $item */
|
||||||
|
foreach ($all as $item) {
|
||||||
|
$this->triggers[$item->id] = $item->key;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private function collectWebhookInfo(): void
|
||||||
|
{
|
||||||
|
$set = DB::table('webhook_webhook_delivery')->whereIn('webhook_id', $this->ids)->get(['webhook_id', 'webhook_delivery_id']);
|
||||||
|
/** @var \stdClass $item */
|
||||||
|
foreach ($set as $item) {
|
||||||
|
$id = $item->webhook_id;
|
||||||
|
$deliveryId = $item->webhook_delivery_id;
|
||||||
|
$this->webhookDeliveries[$id][] = WebhookDeliveryEnum::from($this->deliveries[$deliveryId])->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
$set = DB::table('webhook_webhook_response')->whereIn('webhook_id', $this->ids)->get(['webhook_id', 'webhook_response_id']);
|
||||||
|
/** @var \stdClass $item */
|
||||||
|
foreach ($set as $item) {
|
||||||
|
$id = $item->webhook_id;
|
||||||
|
$responseId = $item->webhook_response_id;
|
||||||
|
$this->webhookResponses[$id][] = WebhookResponseEnum::from($this->responses[$responseId])->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
$set = DB::table('webhook_webhook_trigger')->whereIn('webhook_id', $this->ids)->get(['webhook_id', 'webhook_trigger_id']);
|
||||||
|
/** @var \stdClass $item */
|
||||||
|
foreach ($set as $item) {
|
||||||
|
$id = $item->webhook_id;
|
||||||
|
$triggerId = $item->webhook_trigger_id;
|
||||||
|
$this->webhookTriggers[$id][] = WebhookTriggerEnum::from($this->triggers[$triggerId])->name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function appendCollectedInfo(): void
|
||||||
|
{
|
||||||
|
$this->collection = $this->collection->map(function (Webhook $item) {
|
||||||
|
$meta = [
|
||||||
|
'deliveries' => $this->webhookDeliveries[$item->id] ?? [],
|
||||||
|
'responses' => $this->webhookResponses[$item->id] ?? [],
|
||||||
|
'triggers' => $this->webhookTriggers[$item->id] ?? [],
|
||||||
|
];
|
||||||
|
$item->meta = $meta;
|
||||||
|
return $item;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -51,9 +51,12 @@ class WebhookTransformer extends AbstractTransformer
|
|||||||
'active' => $webhook->active,
|
'active' => $webhook->active,
|
||||||
'title' => $webhook->title,
|
'title' => $webhook->title,
|
||||||
'secret' => $webhook->secret,
|
'secret' => $webhook->secret,
|
||||||
'trigger' => $this->getEnum('trigger', $webhook->trigger),
|
'triggers' => $webhook->meta['triggers'],
|
||||||
'response' => $this->getEnum('response', $webhook->response),
|
'deliveries' => $webhook->meta['deliveries'],
|
||||||
'delivery' => $this->getEnum('delivery', $webhook->delivery),
|
'responses' => $webhook->meta['responses'],
|
||||||
|
// 'trigger' => $this->getEnum('trigger', $webhook->trigger),
|
||||||
|
// 'response' => $this->getEnum('response', $webhook->response),
|
||||||
|
// 'delivery' => $this->getEnum('delivery', $webhook->delivery),
|
||||||
'url' => $webhook->url,
|
'url' => $webhook->url,
|
||||||
'links' => [
|
'links' => [
|
||||||
[
|
[
|
||||||
|
@@ -0,0 +1,127 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\QueryException;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration {
|
||||||
|
private const TABLE_ALREADY_EXISTS = 'If this table exists already (see the error message), this is not a problem. Other errors? Please open a discussion on GitHub.';
|
||||||
|
private const TABLE_ERROR = 'Could not create table "%s": %s';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
if (!Schema::hasTable('webhook_triggers')) {
|
||||||
|
Schema::create('webhook_triggers', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->timestamps();
|
||||||
|
$table->smallInteger('key')->unsigned();
|
||||||
|
$table->string('title', 100);
|
||||||
|
$table->unique(['key', 'title']);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (!Schema::hasTable('webhook_responses')) {
|
||||||
|
Schema::create('webhook_responses', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->timestamps();
|
||||||
|
$table->smallInteger('key')->unsigned();
|
||||||
|
$table->string('title', 100);
|
||||||
|
$table->unique(['key', 'title']);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (!Schema::hasTable('webhook_deliveries')) {
|
||||||
|
Schema::create('webhook_deliveries', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->timestamps();
|
||||||
|
$table->smallInteger('key')->unsigned();
|
||||||
|
$table->string('title', 100);
|
||||||
|
$table->unique(['key', 'title']);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// webhook_webhook_trigger
|
||||||
|
if (!Schema::hasTable('webhook_webhook_trigger')) {
|
||||||
|
try {
|
||||||
|
Schema::create(
|
||||||
|
'webhook_webhook_trigger',
|
||||||
|
static function (Blueprint $table): void {
|
||||||
|
$table->increments('id');
|
||||||
|
$table->integer('webhook_id', false, true);
|
||||||
|
$table->bigInteger('webhook_trigger_id', false, true);
|
||||||
|
$table->foreign('webhook_id')->references('id')->on('webhooks')->onDelete('cascade');
|
||||||
|
$table->foreign('webhook_trigger_id','link_to_trigger')->references('id')->on('webhook_triggers')->onDelete('cascade');
|
||||||
|
|
||||||
|
// unique combi:
|
||||||
|
$table->unique(['webhook_id', 'webhook_trigger_id']);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} catch (QueryException $e) {
|
||||||
|
app('log')->error(sprintf(self::TABLE_ERROR, 'webhook_webhook_trigger', $e->getMessage()));
|
||||||
|
app('log')->error(self::TABLE_ALREADY_EXISTS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// webhook_webhook_response
|
||||||
|
if (!Schema::hasTable('webhook_webhook_response')) {
|
||||||
|
try {
|
||||||
|
Schema::create(
|
||||||
|
'webhook_webhook_response',
|
||||||
|
static function (Blueprint $table): void {
|
||||||
|
$table->increments('id');
|
||||||
|
$table->integer('webhook_id', false, true);
|
||||||
|
$table->bigInteger('webhook_response_id', false, true);
|
||||||
|
$table->foreign('webhook_id')->references('id')->on('webhooks')->onDelete('cascade');
|
||||||
|
$table->foreign('webhook_response_id','link_to_response')->references('id')->on('webhook_responses')->onDelete('cascade');
|
||||||
|
|
||||||
|
// unique combi:
|
||||||
|
$table->unique(['webhook_id', 'webhook_response_id']);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} catch (QueryException $e) {
|
||||||
|
app('log')->error(sprintf(self::TABLE_ERROR, 'webhook_webhook_response', $e->getMessage()));
|
||||||
|
app('log')->error(self::TABLE_ALREADY_EXISTS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// webhook_webhook_delivery
|
||||||
|
if (!Schema::hasTable('webhook_webhook_delivery')) {
|
||||||
|
try {
|
||||||
|
Schema::create(
|
||||||
|
'webhook_webhook_delivery',
|
||||||
|
static function (Blueprint $table): void {
|
||||||
|
$table->increments('id');
|
||||||
|
$table->integer('webhook_id', false, true);
|
||||||
|
$table->bigInteger('webhook_delivery_id', false, true);
|
||||||
|
$table->foreign('webhook_id')->references('id')->on('webhooks')->onDelete('cascade');
|
||||||
|
$table->foreign('webhook_delivery_id','link_to_delivery')->references('id')->on('webhook_deliveries')->onDelete('cascade');
|
||||||
|
|
||||||
|
// unique combi:
|
||||||
|
$table->unique(['webhook_id', 'webhook_delivery_id']);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} catch (QueryException $e) {
|
||||||
|
app('log')->error(sprintf(self::TABLE_ERROR, 'webhook_webhook_delivery', $e->getMessage()));
|
||||||
|
app('log')->error(self::TABLE_ALREADY_EXISTS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('webhook_webhook_delivery');
|
||||||
|
Schema::dropIfExists('webhook_webhook_trigger');
|
||||||
|
Schema::dropIfExists('webhook_webhook_response');
|
||||||
|
|
||||||
|
Schema::dropIfExists('webhook_triggers');
|
||||||
|
Schema::dropIfExists('webhook_responses');
|
||||||
|
Schema::dropIfExists('webhook_deliveries');
|
||||||
|
}
|
||||||
|
};
|
@@ -35,25 +35,10 @@ class AccountTypeSeeder extends Seeder
|
|||||||
{
|
{
|
||||||
public function run(): void
|
public function run(): void
|
||||||
{
|
{
|
||||||
$types = [
|
foreach(AccountTypeEnum::cases() as $type) {
|
||||||
AccountTypeEnum::DEFAULT->value,
|
if (null === AccountType::where('type', $type->value)->first()) {
|
||||||
AccountTypeEnum::CASH->value,
|
|
||||||
AccountTypeEnum::ASSET->value,
|
|
||||||
AccountTypeEnum::EXPENSE->value,
|
|
||||||
AccountTypeEnum::REVENUE->value,
|
|
||||||
AccountTypeEnum::INITIAL_BALANCE->value,
|
|
||||||
AccountTypeEnum::BENEFICIARY->value,
|
|
||||||
AccountTypeEnum::IMPORT->value,
|
|
||||||
AccountTypeEnum::LOAN->value,
|
|
||||||
AccountTypeEnum::RECONCILIATION->value,
|
|
||||||
AccountTypeEnum::DEBT->value,
|
|
||||||
AccountTypeEnum::MORTGAGE->value,
|
|
||||||
AccountTypeEnum::LIABILITY_CREDIT->value,
|
|
||||||
];
|
|
||||||
foreach ($types as $type) {
|
|
||||||
if (null === AccountType::where('type', $type)->first()) {
|
|
||||||
try {
|
try {
|
||||||
AccountType::create(['type' => $type]);
|
AccountType::create(['type' => $type->value]);
|
||||||
} catch (PDOException $e) {
|
} catch (PDOException $e) {
|
||||||
// @ignoreException
|
// @ignoreException
|
||||||
}
|
}
|
||||||
|
@@ -43,5 +43,6 @@ class DatabaseSeeder extends Seeder
|
|||||||
$this->call(ConfigSeeder::class);
|
$this->call(ConfigSeeder::class);
|
||||||
$this->call(UserRoleSeeder::class);
|
$this->call(UserRoleSeeder::class);
|
||||||
$this->call(ExchangeRateSeeder::class);
|
$this->call(ExchangeRateSeeder::class);
|
||||||
|
$this->call(WebhookDataSeeder::class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -35,24 +35,17 @@ class TransactionTypeSeeder extends Seeder
|
|||||||
{
|
{
|
||||||
public function run(): void
|
public function run(): void
|
||||||
{
|
{
|
||||||
$types = [
|
/** @var TransactionTypeEnum $type */
|
||||||
TransactionTypeEnum::WITHDRAWAL->value,
|
foreach (TransactionTypeEnum::cases() as $type) {
|
||||||
TransactionTypeEnum::DEPOSIT->value,
|
if (null === TransactionType::where('type', $type->value)->first()) {
|
||||||
TransactionTypeEnum::TRANSFER->value,
|
|
||||||
TransactionTypeEnum::OPENING_BALANCE->value,
|
|
||||||
TransactionTypeEnum::RECONCILIATION->value,
|
|
||||||
TransactionTypeEnum::INVALID->value,
|
|
||||||
TransactionTypeEnum::LIABILITY_CREDIT->value,
|
|
||||||
];
|
|
||||||
|
|
||||||
foreach ($types as $type) {
|
|
||||||
if (null === TransactionType::where('type', $type)->first()) {
|
|
||||||
try {
|
try {
|
||||||
TransactionType::create(['type' => $type]);
|
TransactionType::create(['type' => $type->value]);
|
||||||
} catch (PDOException $e) {
|
} catch (PDOException $e) {
|
||||||
// @ignoreException
|
// @ignoreException
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -39,16 +39,11 @@ class UserRoleSeeder extends Seeder
|
|||||||
*/
|
*/
|
||||||
public function run(): void
|
public function run(): void
|
||||||
{
|
{
|
||||||
$roles = [];
|
/** @var UserRoleEnum $role */
|
||||||
foreach (UserRoleEnum::cases() as $role) {
|
foreach (UserRoleEnum::cases() as $role) {
|
||||||
$roles[] = $role->value;
|
if (null === UserRole::where('title', $role->value)->first()) {
|
||||||
}
|
|
||||||
|
|
||||||
/** @var string $role */
|
|
||||||
foreach ($roles as $role) {
|
|
||||||
if (null === UserRole::where('title', $role)->first()) {
|
|
||||||
try {
|
try {
|
||||||
UserRole::create(['title' => $role]);
|
UserRole::create(['title' => $role->value]);
|
||||||
} catch (PDOException $e) {
|
} catch (PDOException $e) {
|
||||||
// @ignoreException
|
// @ignoreException
|
||||||
}
|
}
|
||||||
|
48
database/seeders/WebhookDataSeeder.php
Normal file
48
database/seeders/WebhookDataSeeder.php
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Seeders;
|
||||||
|
|
||||||
|
use FireflyIII\Enums\WebhookTrigger;
|
||||||
|
use FireflyIII\Enums\WebhookResponse;
|
||||||
|
use FireflyIII\Enums\WebhookDelivery;
|
||||||
|
use FireflyIII\Models\WebhookTrigger as WebhookTriggerModel;
|
||||||
|
use FireflyIII\Models\WebhookResponse as WebhookResponseModel;
|
||||||
|
use FireflyIII\Models\WebhookDelivery as WebhookDeliveryModel;
|
||||||
|
use Illuminate\Database\Seeder;
|
||||||
|
|
||||||
|
class WebhookDataSeeder extends Seeder
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the database seeds.
|
||||||
|
*/
|
||||||
|
public function run(): void
|
||||||
|
{
|
||||||
|
foreach (WebhookTrigger::cases() as $trigger) {
|
||||||
|
if (null === WebhookTriggerModel::where('key', $trigger->value)->where('title', $trigger->name)->first()) {
|
||||||
|
try {
|
||||||
|
WebhookTriggerModel::create(['key' => $trigger->value, 'title' => $trigger->name]);
|
||||||
|
} catch (\PDOException $e) {
|
||||||
|
// @ignoreException
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach (WebhookResponse::cases() as $response) {
|
||||||
|
if (null === WebhookResponseModel::where('key', $response->value)->where('title', $response->name)->first()) {
|
||||||
|
try {
|
||||||
|
WebhookResponseModel::create(['key' => $response->value, 'title' => $response->name]);
|
||||||
|
} catch (\PDOException $e) {
|
||||||
|
// @ignoreException
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach (WebhookDelivery::cases() as $delivery) {
|
||||||
|
if (null === WebhookDeliveryModel::where('key', $delivery->value)->where('title', $delivery->name)->first()) {
|
||||||
|
try {
|
||||||
|
WebhookDeliveryModel::create(['key' => $delivery->value, 'title' => $delivery->name]);
|
||||||
|
} catch (\PDOException $e) {
|
||||||
|
// @ignoreException
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user