diff --git a/.env.example b/.env.example index 660691fa..5ca7fbe7 100644 --- a/.env.example +++ b/.env.example @@ -62,7 +62,13 @@ DISCORD_AUTH_TOKEN=abc DISCORD_WEBHOOK_URL=def DISCORD_USERNAME=FlowBot DISCORD_AVATAR_URL="${APP_URL}/images/logo.png" +DISCORD_ECFMP_CHANNEL_ID="971531731096203364" # Sentry monitoring SENTRY_LARAVEL_DSN= SENTRY_TRACES_SAMPLE_RATE=1.0 + +# Discord bot +DISCORD_BOT_SERVICE_URL="discord-bot:80" +DISCORD_BOT_JWT="eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJlY2ZtcC1mbG93IiwiYXVkIjoiZWNmbXAtZGlzY29yZC1kZXYiLCJpYXQiOjIxMzg0NTQ1NTUsImlzcyI6ImVjZm1wLWF1dGgifQ.PqbCEe_zW1WBLio6aD0P5OksRI1H-hoRRA8168OJg-h11SjyZeDCAAf0CjgZAUy6vUpSdfgN9KH1SgCSM4J38-2txpZLr_VlJTu9_W1mGEVr1_pGjMgbkwx8PMTP1f3J2R0BwGz-324vpPVB9zu6NG9ujS48AD28mDoqMpqc7UOK0_e9WJ7cQBb8BxU10w4TQXbwhjUMyZBIpdiaDK5OsQeXJruo0OjSlltiFJkXPmESTz_DwwTSvIqzmhjzQfNW62RVcnBrnbWaaCg1mC6FSIMffjrEgian_AyAg1iftjy_fa3f-sU-z65xMh8vVwAvJEhYJCA0CZO4lKn_OV0RXqYjCLI4t-Rp6MYULyLbp6QZ88MOvSXd-8GnYnxDE5o5-rLFnQ04LCGx2-yBDPZ80brdxZR26Im1DrNiPyUFadINGf8wwZ4-iWqmY6_QSfJYU1C3Y5s7TxMBFfa934NHICg53gVSEdfCHQIaciOSu91P1FnGIvdtOQV_n7urF5HRYyxOUomSa4MY4m5C1-TJqpBkCsAXbMdC_mIllFWnUCB5uBj5T18mxW1mrGQiF3Vy6_MrSeFoY0LEnd58QpvnG7-OuZWnrIbDDTAnTcI1IMVUA4zcoUvkUcCcRvBQD2bXsPgL9Lh8RmYvjrR2hWhmlJU-k_CPiQOLfW9qOFGp5rA" +DISCORD_CLIENT_REQUEST_APP_ID="ecfmpdev" diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 97c3abbc..d8f0eb70 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -21,7 +21,21 @@ jobs: composer: ["v2"] steps: - name: Checkout Code - uses: actions/checkout@v2 + uses: actions/checkout@v4 + with: + submodules: true + + - name: Checkout Discord Service + uses: actions/checkout@v4 + with: + repository: ecfmp/discord + path: ecfmp-discord + + - name: Move Discord Service + run: mv ecfmp-discord ../ecfmp-discord + + - name: Build Protobuf + run: (cd protobuf && make pull_builder && make discord_proto) - name: Configure PHP uses: shivammathur/setup-php@v2 @@ -29,6 +43,7 @@ jobs: php-version: ${{ matrix.php }} coverage: pcov tools: composer:${{ matrix.composer }} + extensions: grpc # Setting up composer dependencies - name: Get Composer Cache Directory diff --git a/.gitignore b/.gitignore index 62af7d63..fa746c90 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,7 @@ Homestead.yaml Homestead.json /.vagrant .phpunit.result.cache +.phpunit.cache .idea # Laravel IDE Helper @@ -39,3 +40,5 @@ public/mix-manifest.json storage/imports .vscode + +.DS_STORE diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..f1b83d04 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "protobuf"] + path = protobuf + url = git@github.com:ECFMP/ecfmp-protobuf.git diff --git a/app/Console/Commands/SendEcfmpDiscordMessages.php b/app/Console/Commands/SendEcfmpDiscordMessages.php new file mode 100644 index 00000000..6a119988 --- /dev/null +++ b/app/Console/Commands/SendEcfmpDiscordMessages.php @@ -0,0 +1,38 @@ +generateAndSend(); + Log::info('Discord notification sending complete'); + + return 0; + } +} diff --git a/app/Discord/Client/ClientFactory.php b/app/Discord/Client/ClientFactory.php new file mode 100644 index 00000000..ea2b4a60 --- /dev/null +++ b/app/Discord/Client/ClientFactory.php @@ -0,0 +1,32 @@ +client === null) { + $this->client = new DiscordClient( + config('discord.service_host'), + [ + 'credentials' => ChannelCredentials::createInsecure(), + 'grpc.primary_user_agent' => config('app.name'), + ], + ); + } + + return $this->client; + } +} diff --git a/app/Discord/Client/ClientFactoryInterface.php b/app/Discord/Client/ClientFactoryInterface.php new file mode 100644 index 00000000..48efd27f --- /dev/null +++ b/app/Discord/Client/ClientFactoryInterface.php @@ -0,0 +1,10 @@ +discordClientFactory = $discordClientFactory; + } + + public function sendMessage(string $clientRequestId, EcfmpMessageInterface $message): string + { + $client = $this->discordClientFactory->create(); + + // Wait for 1 second for the channel to be ready + $channelReady = $client->waitForReady(1000000); + if (!$channelReady) { + Log::error('Discord grpc channel not ready'); + throw new DiscordServiceException('Discord grpc channel not ready'); + } + + /** + * @var $response \Ecfmp_discord\CreateResponse + */ + [$response, $status] = $client->Create( + new CreateRequest( + [ + 'channel' => $message->channel(), + 'content' => $message->content(), + 'embeds' => $message->embeds()->toProtobuf(), + ] + ), + [ + 'authorization' => [config('discord.service_token')], + 'x-client-request-id' => [$clientRequestId], + ], + )->wait(); + + if ($status->code !== STATUS_OK) { + Log::error('Discord grpc call failed', [ + 'code' => $status->code, + 'details' => $status->details, + ]); + + throw new DiscordServiceException('Discord grpc call failed'); + } + + return $response->getId(); + } +} diff --git a/app/Discord/DiscordInterface.php b/app/Discord/DiscordWebhookInterface.php similarity index 67% rename from app/Discord/DiscordInterface.php rename to app/Discord/DiscordWebhookInterface.php index cdae13dd..1a0ca854 100644 --- a/app/Discord/DiscordInterface.php +++ b/app/Discord/DiscordWebhookInterface.php @@ -7,9 +7,9 @@ /** * To hide the details of how we go about doing Discord things... * - * This class is the interface for interacting with Discord. + * This class is the interface for interacting with Discord via webhooks. */ -interface DiscordInterface +interface DiscordWebhookInterface { public function sendMessage(MessageInterface $message): bool; } diff --git a/app/Discord/DiscordMessageSender.php b/app/Discord/DiscordWebhookSender.php similarity index 95% rename from app/Discord/DiscordMessageSender.php rename to app/Discord/DiscordWebhookSender.php index fc25755e..217151b2 100644 --- a/app/Discord/DiscordMessageSender.php +++ b/app/Discord/DiscordWebhookSender.php @@ -6,7 +6,7 @@ use Illuminate\Support\Facades\Http; use Log; -class DiscordMessageSender implements DiscordInterface +class DiscordWebhookSender implements DiscordWebhookInterface { public function sendMessage(MessageInterface $message): bool { diff --git a/app/Discord/Exception/DiscordServiceException.php b/app/Discord/Exception/DiscordServiceException.php new file mode 100644 index 00000000..766bad31 --- /dev/null +++ b/app/Discord/Exception/DiscordServiceException.php @@ -0,0 +1,9 @@ +flowMeasure->discordNotifications()->attach( + $this->flowMeasure->divisionDiscordNotifications()->attach( [ $notification->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum($this->type), diff --git a/app/Discord/FlowMeasure/Content/FlowMeasureRecipientsFactory.php b/app/Discord/FlowMeasure/Content/FlowMeasureRecipientsFactory.php index a7daecb1..0c277092 100644 --- a/app/Discord/FlowMeasure/Content/FlowMeasureRecipientsFactory.php +++ b/app/Discord/FlowMeasure/Content/FlowMeasureRecipientsFactory.php @@ -3,9 +3,11 @@ namespace App\Discord\FlowMeasure\Content; use App\Discord\FlowMeasure\Provider\PendingMessageInterface; +use App\Discord\FlowMeasure\Provider\PendingWebhookMessageInterface; use App\Discord\Message\Tag\Tag; use App\Enums\DiscordNotificationType; use App\Models\DiscordNotification; +use App\Models\DivisionDiscordNotification; use App\Models\DiscordTag; use App\Models\DivisionDiscordWebhook; use App\Models\FlightInformationRegion; @@ -13,22 +15,39 @@ class FlowMeasureRecipientsFactory { - public function makeRecipients(PendingMessageInterface $pendingMessage): FlowMeasureRecipientsInterface + public function makeRecipients(PendingWebhookMessageInterface $pendingMessage): FlowMeasureRecipientsInterface + { + if ($this->hasRecentlyBeenNotifiedToWebhook($pendingMessage)) { + return new NoRecipients(); + } + + return $this->divisionRecipients($pendingMessage); + } + + public function makeEcfmpRecipients(PendingMessageInterface $pendingMessage): FlowMeasureRecipientsInterface { if ($this->hasRecentlyBeenNotified($pendingMessage)) { return new NoRecipients(); } - return $pendingMessage->webhook()->id() === null - ? $this->ecfmpRecipients($pendingMessage) - : $this->divisionRecipients($pendingMessage); + return $this->ecfmpRecipients($pendingMessage); + } + + private function hasRecentlyBeenNotifiedToWebhook(PendingWebhookMessageInterface $pendingMessage): bool + { + $measure = $pendingMessage->flowMeasure(); + return $pendingMessage->type( + ) === DiscordNotificationType::FLOW_MEASURE_ACTIVATED && $measure->notifiedDivisionNotifications->firstWhere( + fn (DivisionDiscordNotification $notification) => $notification->created_at > Carbon::now()->subHour() && + $notification->pivot->notified_as === $measure->identifier + ) !== null; } private function hasRecentlyBeenNotified(PendingMessageInterface $pendingMessage): bool { $measure = $pendingMessage->flowMeasure(); return $pendingMessage->type( - ) === DiscordNotificationType::FLOW_MEASURE_ACTIVATED && $measure->notifiedDiscordNotifications->firstWhere( + ) === DiscordNotificationType::FLOW_MEASURE_ACTIVATED && $measure->notifiedEcfmpNotifications->firstWhere( fn (DiscordNotification $notification) => $notification->created_at > Carbon::now()->subHour() && $notification->pivot->notified_as === $measure->identifier ) !== null; @@ -44,7 +63,7 @@ private function ecfmpRecipients(PendingMessageInterface $pendingMessage): FlowM ); } - private function divisionRecipients(PendingMessageInterface $pendingMessage): FlowMeasureRecipientsInterface + private function divisionRecipients(PendingWebhookMessageInterface $pendingMessage): FlowMeasureRecipientsInterface { $recipients = DivisionDiscordWebhook::find($pendingMessage->webhook()->id()) ->flightInformationRegions diff --git a/app/Discord/FlowMeasure/Embed/ActivatedEmbeds.php b/app/Discord/FlowMeasure/Embed/ActivatedEmbeds.php index 58f5cc65..f46b1b8c 100644 --- a/app/Discord/FlowMeasure/Embed/ActivatedEmbeds.php +++ b/app/Discord/FlowMeasure/Embed/ActivatedEmbeds.php @@ -39,7 +39,7 @@ public function embeds(): EmbedCollection : IdentifierAndActiveStatus::create($this->pendingMessage->flowMeasure()) ) ->withDescription(new EventName($this->pendingMessage->flowMeasure())) - ->withField(Field::make(new IssuingUser($this->pendingMessage->flowMeasure())), is_null($this->pendingMessage->webhook()->id())) + ->withField(Field::make(new IssuingUser($this->pendingMessage->flowMeasure())), $this->pendingMessage->isEcfmp()) ->withField(Field::makeInline(new Restriction($this->pendingMessage->flowMeasure()))) ->withField(Field::makeInline(new StartTime($this->pendingMessage->flowMeasure()))) ->withField(Field::makeInline(new EndTime($this->pendingMessage->flowMeasure()))) diff --git a/app/Discord/FlowMeasure/Embed/NotifiedEmbeds.php b/app/Discord/FlowMeasure/Embed/NotifiedEmbeds.php index 397dcd13..64b5763b 100644 --- a/app/Discord/FlowMeasure/Embed/NotifiedEmbeds.php +++ b/app/Discord/FlowMeasure/Embed/NotifiedEmbeds.php @@ -39,7 +39,7 @@ public function embeds(): EmbedCollection : IdentifierAndNotifiedStatus::create($this->pendingMessage->flowMeasure()) ) ->withDescription(new EventName($this->pendingMessage->flowMeasure())) - ->withField(Field::make(new IssuingUser($this->pendingMessage->flowMeasure())), is_null($this->pendingMessage->webhook()->id())) + ->withField(Field::make(new IssuingUser($this->pendingMessage->flowMeasure())), $this->pendingMessage->isEcfmp()) ->withField(Field::makeInline(new Restriction($this->pendingMessage->flowMeasure()))) ->withField(Field::makeInline(new StartTime($this->pendingMessage->flowMeasure()))) ->withField(Field::makeInline(new EndTime($this->pendingMessage->flowMeasure()))) diff --git a/app/Discord/FlowMeasure/Generator/EcfmpFlowMeasureMessageGenerator.php b/app/Discord/FlowMeasure/Generator/EcfmpFlowMeasureMessageGenerator.php new file mode 100644 index 00000000..bf86fff6 --- /dev/null +++ b/app/Discord/FlowMeasure/Generator/EcfmpFlowMeasureMessageGenerator.php @@ -0,0 +1,37 @@ +sender = $sender; + $this->repositories = $repositories; + } + + public function generateAndSend(): void + { + foreach ($this->repositories as $repository) { + foreach ($repository->flowMeasuresToBeSentToEcfmp() as $measure) { + $pendingMessage = new PendingEcfmpMessage( + $measure->measure, + $repository->notificationType(), + new EcfmpNotificationReissuer($measure, $repository->notificationType()) + ); + + $this->sender->send($pendingMessage); + } + } + } +} diff --git a/app/Discord/FlowMeasure/Helper/EcfmpNotificationReissuer.php b/app/Discord/FlowMeasure/Helper/EcfmpNotificationReissuer.php new file mode 100644 index 00000000..7482fb91 --- /dev/null +++ b/app/Discord/FlowMeasure/Helper/EcfmpNotificationReissuer.php @@ -0,0 +1,24 @@ +flowMeasureForNotification = $flowMeasureForNotification; + $this->type = $type; + } + + public function isReissuedNotification(): bool + { + return ($this->type === DiscordNotificationType::FLOW_MEASURE_ACTIVATED || $this->type === DiscordNotificationType::FLOW_MEASURE_NOTIFIED) + && $this->flowMeasureForNotification->isReissuedNotification; + } +} diff --git a/app/Discord/FlowMeasure/Helper/NotificationReissuer.php b/app/Discord/FlowMeasure/Helper/NotificationReissuer.php index 025386c0..bf51bbee 100644 --- a/app/Discord/FlowMeasure/Helper/NotificationReissuer.php +++ b/app/Discord/FlowMeasure/Helper/NotificationReissuer.php @@ -4,7 +4,7 @@ use App\Discord\Webhook\WebhookInterface; use App\Enums\DiscordNotificationType; -use App\Models\DiscordNotification; +use App\Models\DivisionDiscordNotification; use App\Models\FlowMeasure; class NotificationReissuer implements NotificationReissuerInterface @@ -20,6 +20,7 @@ public function __construct(FlowMeasure $measure, DiscordNotificationType $type, $this->webhook = $webhook; } + // TODO: Update this public function isReissuedNotification(): bool { if ( @@ -29,17 +30,17 @@ public function isReissuedNotification(): bool return false; } - $notificationsOfType = $this->measure->activatedAndNotifiedNotifications() + $notificationsOfType = $this->measure->activatedAndNotifiedDivisionNotifications() ->where('division_discord_webhook_id', $this->webhook->id()) ->get(); return $notificationsOfType->filter( fn ( - DiscordNotification $notification + DivisionDiscordNotification $notification ) => $notification->pivot->notified_as !== $this->measure->identifier )->isNotEmpty() && $notificationsOfType->filter( fn ( - DiscordNotification $notification + DivisionDiscordNotification $notification ) => $notification->pivot->notified_as === $this->measure->identifier )->isEmpty(); } diff --git a/app/Discord/FlowMeasure/Logger/FlowMeasureLogger.php b/app/Discord/FlowMeasure/Logger/FlowMeasureLogger.php index 082baa5d..e71246f7 100644 --- a/app/Discord/FlowMeasure/Logger/FlowMeasureLogger.php +++ b/app/Discord/FlowMeasure/Logger/FlowMeasureLogger.php @@ -4,7 +4,7 @@ use App\Discord\Message\Logger\LoggerInterface; use App\Enums\DiscordNotificationType; -use App\Models\DiscordNotification; +use App\Models\DivisionDiscordNotification; use App\Models\FlowMeasure; class FlowMeasureLogger implements LoggerInterface @@ -18,7 +18,7 @@ public function __construct(FlowMeasure $flowMeasure, DiscordNotificationType $t $this->type = $type; } - public function log(DiscordNotification $notification): void + public function log(DivisionDiscordNotification $notification): void { activity() ->inLog('Discord') diff --git a/app/Discord/FlowMeasure/Message/EcfmpFlowMeasureMessage.php b/app/Discord/FlowMeasure/Message/EcfmpFlowMeasureMessage.php new file mode 100644 index 00000000..1bd9881b --- /dev/null +++ b/app/Discord/FlowMeasure/Message/EcfmpFlowMeasureMessage.php @@ -0,0 +1,37 @@ +channel = $channel; + $this->recipients = $recipients; + $this->embeds = $embeds; + } + + public function channel(): string + { + return $this->channel; + } + + public function content(): string + { + return $this->recipients->toString(); + } + + public function embeds(): EmbedCollection + { + return $this->embeds->embeds(); + } +} diff --git a/app/Discord/FlowMeasure/Message/FlowMeasureMessageFactory.php b/app/Discord/FlowMeasure/Message/FlowMeasureMessageFactory.php index 4ea234ec..1628d683 100644 --- a/app/Discord/FlowMeasure/Message/FlowMeasureMessageFactory.php +++ b/app/Discord/FlowMeasure/Message/FlowMeasureMessageFactory.php @@ -7,6 +7,7 @@ use App\Discord\FlowMeasure\Embed\FlowMeasureEmbedFactory; use App\Discord\FlowMeasure\Logger\FlowMeasureLogger; use App\Discord\FlowMeasure\Provider\PendingMessageInterface; +use App\Discord\FlowMeasure\Provider\PendingWebhookMessageInterface; class FlowMeasureMessageFactory { @@ -19,7 +20,7 @@ public function __construct(FlowMeasureRecipientsFactory $recipientsFactory, Flo $this->embedFactory = $embedFactory; } - public function make(PendingMessageInterface $pendingMessage): FlowMeasureMessage + public function make(PendingWebhookMessageInterface $pendingMessage): FlowMeasureMessage { return new FlowMeasureMessage( $pendingMessage->webhook(), @@ -29,4 +30,14 @@ public function make(PendingMessageInterface $pendingMessage): FlowMeasureMessag new FlowMeasureLogger($pendingMessage->flowMeasure(), $pendingMessage->type()) ); } + + public function makeEcfmp(PendingMessageInterface $message): EcfmpFlowMeasureMessage + { + return new EcfmpFlowMeasureMessage( + config('discord.ecfmp_channel_id'), + $this->recipientsFactory->makeEcfmpRecipients($message), + $this->embedFactory->make($message) + ); + } + } diff --git a/app/Discord/FlowMeasure/Provider/MessageProvider.php b/app/Discord/FlowMeasure/Provider/DivisionWebhookMessageProvider.php similarity index 91% rename from app/Discord/FlowMeasure/Provider/MessageProvider.php rename to app/Discord/FlowMeasure/Provider/DivisionWebhookMessageProvider.php index 6e249b66..0eacda14 100644 --- a/app/Discord/FlowMeasure/Provider/MessageProvider.php +++ b/app/Discord/FlowMeasure/Provider/DivisionWebhookMessageProvider.php @@ -7,7 +7,7 @@ use App\Repository\FlowMeasureNotification\RepositoryInterface; use Illuminate\Support\Collection; -class MessageProvider implements MessageProviderInterface +class DivisionWebhookMessageProvider implements MessageProviderInterface { private readonly RepositoryInterface $repository; private readonly MapperInterface $webhookMapper; @@ -26,7 +26,7 @@ function (Collection $messages) { foreach ($this->repository->flowMeasuresForNotification() as $flowMeasure) { foreach ($this->webhookMapper->mapToWebhooks($flowMeasure) as $webhook) { $messages->push( - new PendingDiscordMessage( + new PendingDiscordWebhookMessage( $flowMeasure, $this->repository->notificationType(), $webhook, diff --git a/app/Discord/FlowMeasure/Provider/PendingDiscordMessage.php b/app/Discord/FlowMeasure/Provider/PendingDiscordWebhookMessage.php similarity index 88% rename from app/Discord/FlowMeasure/Provider/PendingDiscordMessage.php rename to app/Discord/FlowMeasure/Provider/PendingDiscordWebhookMessage.php index 3d7fef81..092ee3fa 100644 --- a/app/Discord/FlowMeasure/Provider/PendingDiscordMessage.php +++ b/app/Discord/FlowMeasure/Provider/PendingDiscordWebhookMessage.php @@ -7,7 +7,7 @@ use App\Enums\DiscordNotificationType; use App\Models\FlowMeasure; -class PendingDiscordMessage implements PendingMessageInterface +class PendingDiscordWebhookMessage implements PendingWebhookMessageInterface { private readonly FlowMeasure $measure; private readonly DiscordNotificationType $type; @@ -45,4 +45,9 @@ public function reissue(): NotificationReissuerInterface { return $this->resissue; } + + public function isEcfmp(): bool + { + return false; + } } diff --git a/app/Discord/FlowMeasure/Provider/PendingEcfmpMessage.php b/app/Discord/FlowMeasure/Provider/PendingEcfmpMessage.php new file mode 100644 index 00000000..1a5a7e63 --- /dev/null +++ b/app/Discord/FlowMeasure/Provider/PendingEcfmpMessage.php @@ -0,0 +1,43 @@ +flowMeasure = $flowMeasure; + $this->type = $type; + $this->reissue = $reissue; + } + + public function flowMeasure(): FlowMeasure + { + return $this->flowMeasure; + } + + public function type(): DiscordNotificationType + { + return $this->type; + } + + public function reissue(): NotificationReissuerInterface + { + return $this->reissue; + } + + public function isEcfmp(): bool + { + return true; + } +} diff --git a/app/Discord/FlowMeasure/Provider/PendingMessageInterface.php b/app/Discord/FlowMeasure/Provider/PendingMessageInterface.php index 42985f1a..ca44c69a 100644 --- a/app/Discord/FlowMeasure/Provider/PendingMessageInterface.php +++ b/app/Discord/FlowMeasure/Provider/PendingMessageInterface.php @@ -3,17 +3,19 @@ namespace App\Discord\FlowMeasure\Provider; use App\Discord\FlowMeasure\Helper\NotificationReissuerInterface; -use App\Discord\Webhook\WebhookInterface; use App\Enums\DiscordNotificationType; use App\Models\FlowMeasure; +/** + * Represents a pending message that needs to be sent out to Discord. + */ interface PendingMessageInterface { public function flowMeasure(): FlowMeasure; public function type(): DiscordNotificationType; - public function webhook(): WebhookInterface; - public function reissue(): NotificationReissuerInterface; + + public function isEcfmp(): bool; } diff --git a/app/Discord/FlowMeasure/Provider/PendingWebhookMessageInterface.php b/app/Discord/FlowMeasure/Provider/PendingWebhookMessageInterface.php new file mode 100644 index 00000000..4bc1b094 --- /dev/null +++ b/app/Discord/FlowMeasure/Provider/PendingWebhookMessageInterface.php @@ -0,0 +1,10 @@ +discordService = $discordService; + $this->messageFactory = $messageFactory; + + if (!config('discord.client_request_app_id')) { + throw new RuntimeException('Discord client request app id is not set'); + } + } + + public function send(PendingMessageInterface $message): void + { + // Make the message + $discordMessage = $this->messageFactory->makeEcfmp($message); + + // Send the message + try { + $discordNotificationId = $this->discordService->sendMessage($this->makeClientRequestId($message), $discordMessage); + } catch (DiscordServiceException $e) { + Log::error('Failed to send Discord message for flow measure'); + return; + } + + // Commit the notification to the database + DB::transaction(function () use ($message, $discordNotificationId) { + $notification = DiscordNotification::create([ + 'remote_id' => $discordNotificationId, + ]); + $message->flowMeasure()->discordNotifications()->attach($notification, [ + 'discord_notification_type_id' => DiscordNotificationType::idFromEnum($message->type()), + 'notified_as' => $message->flowMeasure()->identifier, + ]); + }); + } + + private function makeClientRequestId(PendingMessageInterface $message): string + { + return sprintf( + '%s-%s-%d-%s', + config('discord.client_request_app_id'), + $message->type()->value, + $message->flowMeasure()->id, + $message->flowMeasure()->identifier + ); + } +} diff --git a/app/Discord/FlowMeasure/Webhook/Filter/ActivatedWebhookFilter.php b/app/Discord/FlowMeasure/Webhook/Filter/ActivatedWebhookFilter.php index dbb21bdf..71a1d677 100644 --- a/app/Discord/FlowMeasure/Webhook/Filter/ActivatedWebhookFilter.php +++ b/app/Discord/FlowMeasure/Webhook/Filter/ActivatedWebhookFilter.php @@ -12,8 +12,8 @@ class ActivatedWebhookFilter implements FilterInterface public function shouldUseWebhook(FlowMeasure $flowMeasure, WebhookInterface $webhook): bool { return $this->existingNotificationDoesntExist( - $flowMeasure->activatedDiscordNotifications() - ->where('discord_notification_flow_measure.notified_as', $flowMeasure->identifier), + $flowMeasure->activatedDivisionNotifications() + ->where('division_discord_notification_flow_measure.notified_as', $flowMeasure->identifier), $webhook ); } diff --git a/app/Discord/FlowMeasure/Webhook/Filter/ExpiredWebhookFilter.php b/app/Discord/FlowMeasure/Webhook/Filter/ExpiredWebhookFilter.php index 75311715..46659a4b 100644 --- a/app/Discord/FlowMeasure/Webhook/Filter/ExpiredWebhookFilter.php +++ b/app/Discord/FlowMeasure/Webhook/Filter/ExpiredWebhookFilter.php @@ -4,7 +4,7 @@ use App\Discord\Webhook\WebhookInterface; use App\Helpers\FlowMeasureIdentifierGenerator; -use App\Models\DiscordNotification; +use App\Models\DivisionDiscordNotification; use App\Models\FlowMeasure; use App\Repository\FlowMeasureRepository; use Carbon\Carbon; @@ -37,16 +37,16 @@ public function shouldUseWebhook(FlowMeasure $flowMeasure, WebhookInterface $web } return $this->existingNotificationDoesntExist( - $flowMeasure->withdrawnAndExpiredDiscordNotifications(), + $flowMeasure->withdrawnAndExpiredDivisionNotifications(), $webhook ); } private function notManyWebhooksRecentlySent(): bool { - return DiscordNotification::where('created_at', '>=', Carbon::now()->subHours(2)) - ->whereNull('division_discord_webhook_id') - ->count() <= 5; + return DivisionDiscordNotification::where('created_at', '>=', Carbon::now()->subHours(2)) + ->whereNull('division_discord_webhook_id') + ->count() <= 5; } private function notRevisedMoreThanOnce(FlowMeasure $flowMeasure): bool @@ -57,7 +57,7 @@ private function notRevisedMoreThanOnce(FlowMeasure $flowMeasure): bool private function lessThanThreeActiveMeasures(FlowMeasure $measure): bool { return $this->flowMeasureRepository->getFlowMeasuresActiveDuringPeriod($measure->start_time, $measure->end_time) - ->reject(fn (FlowMeasure $activeMeasure) => $activeMeasure->id === $measure->id) - ->count() < 3; + ->reject(fn (FlowMeasure $activeMeasure) => $activeMeasure->id === $measure->id) + ->count() < 3; } } diff --git a/app/Discord/FlowMeasure/Webhook/Filter/NotifiedWebhookFilter.php b/app/Discord/FlowMeasure/Webhook/Filter/NotifiedWebhookFilter.php index 69e22a40..01bf4144 100644 --- a/app/Discord/FlowMeasure/Webhook/Filter/NotifiedWebhookFilter.php +++ b/app/Discord/FlowMeasure/Webhook/Filter/NotifiedWebhookFilter.php @@ -20,8 +20,8 @@ public function shouldUseWebhook(FlowMeasure $flowMeasure, WebhookInterface $web private function notYetNotified(FlowMeasure $flowMeasure, WebhookInterface $webhook): bool { return $this->existingNotificationDoesntExist( - $flowMeasure->notifiedDiscordNotifications() - ->where('discord_notification_flow_measure.notified_as', $flowMeasure->identifier), + $flowMeasure->notifiedDivisionNotifications() + ->where('division_discord_notification_flow_measure.notified_as', $flowMeasure->identifier), $webhook ); } @@ -29,7 +29,7 @@ private function notYetNotified(FlowMeasure $flowMeasure, WebhookInterface $webh private function notYetActivated(FlowMeasure $flowMeasure, WebhookInterface $webhook): bool { return $this->existingNotificationDoesntExist( - $flowMeasure->activatedDiscordNotifications(), + $flowMeasure->activatedDivisionNotifications(), $webhook ); } diff --git a/app/Discord/FlowMeasure/Webhook/Filter/WithdrawnWebhookFilter.php b/app/Discord/FlowMeasure/Webhook/Filter/WithdrawnWebhookFilter.php index 3ee0c327..f9fafb2e 100644 --- a/app/Discord/FlowMeasure/Webhook/Filter/WithdrawnWebhookFilter.php +++ b/app/Discord/FlowMeasure/Webhook/Filter/WithdrawnWebhookFilter.php @@ -18,7 +18,7 @@ public function shouldUseWebhook(FlowMeasure $flowMeasure, WebhookInterface $web private function hasBeenActivatedOrNotified(FlowMeasure $flowMeasure, WebhookInterface $webhook): bool { return $this->existingNotificationExists( - $flowMeasure->activatedAndNotifiedNotifications(), + $flowMeasure->activatedAndNotifiedDivisionNotifications(), $webhook ); } @@ -26,7 +26,7 @@ private function hasBeenActivatedOrNotified(FlowMeasure $flowMeasure, WebhookInt private function hasNotBeenWithdrawnOrExpired(FlowMeasure $flowMeasure, WebhookInterface $webhook): bool { return $this->existingNotificationDoesntExist( - $flowMeasure->withdrawnAndExpiredDiscordNotifications(), + $flowMeasure->withdrawnAndExpiredDivisionNotifications(), $webhook ); } diff --git a/app/Discord/Message/Associator/AssociatorInterface.php b/app/Discord/Message/Associator/AssociatorInterface.php index e43aa786..f9448c9e 100644 --- a/app/Discord/Message/Associator/AssociatorInterface.php +++ b/app/Discord/Message/Associator/AssociatorInterface.php @@ -2,9 +2,9 @@ namespace App\Discord\Message\Associator; -use App\Models\DiscordNotification; +use App\Models\DivisionDiscordNotification; interface AssociatorInterface { - public function associate(DiscordNotification $notification): void; + public function associate(DivisionDiscordNotification $notification): void; } diff --git a/app/Discord/Message/EcfmpMessageInterface.php b/app/Discord/Message/EcfmpMessageInterface.php new file mode 100644 index 00000000..0abec238 --- /dev/null +++ b/app/Discord/Message/EcfmpMessageInterface.php @@ -0,0 +1,26 @@ +title)) { + $embed->setTitle($this->title->title()); + } + + if (isset($this->colour)) { + $embed->setColor($this->colour->value); + } + + if (isset($this->description)) { + $embed->setDescription($this->description->description()); + } + + if ($this->fields->isNotEmpty()) { + $embed->setFields( + $this->fields->map(fn (FieldInterface $field) => new DiscordEmbedsFields([ + 'name' => $field->name(), + 'value' => $field->value(), + 'inline' => $field->inline(), + ]))->toArray() + ); + } + } + ); + } } diff --git a/app/Discord/Message/Embed/EmbedCollection.php b/app/Discord/Message/Embed/EmbedCollection.php index 8a03ae0f..0369f33f 100644 --- a/app/Discord/Message/Embed/EmbedCollection.php +++ b/app/Discord/Message/Embed/EmbedCollection.php @@ -29,4 +29,9 @@ public function toArray(): array { return $this->embeds->map(fn (EmbedInterface $embed) => $embed->toArray())->toArray(); } + + public function toProtobuf(): array + { + return $this->embeds->map(fn (EmbedInterface $embed) => $embed->toProtobuf())->toArray(); + } } diff --git a/app/Discord/Message/Embed/EmbedInterface.php b/app/Discord/Message/Embed/EmbedInterface.php index 2bda0f4f..8901332f 100644 --- a/app/Discord/Message/Embed/EmbedInterface.php +++ b/app/Discord/Message/Embed/EmbedInterface.php @@ -2,10 +2,17 @@ namespace App\Discord\Message\Embed; +use Ecfmp_discord\DiscordEmbeds; + interface EmbedInterface { /** * Converts the embed to array. */ public function toArray(): array; + + /** + * Converts the embed to protobuf format. + */ + public function toProtobuf(): DiscordEmbeds; } diff --git a/app/Discord/Message/Logger/LoggerInterface.php b/app/Discord/Message/Logger/LoggerInterface.php index 7adca40b..b149abae 100644 --- a/app/Discord/Message/Logger/LoggerInterface.php +++ b/app/Discord/Message/Logger/LoggerInterface.php @@ -2,9 +2,9 @@ namespace App\Discord\Message\Logger; -use App\Models\DiscordNotification; +use App\Models\DivisionDiscordNotification; interface LoggerInterface { - public function log(DiscordNotification $notification): void; + public function log(DivisionDiscordNotification $notification): void; } diff --git a/app/Discord/Message/Sender/Sender.php b/app/Discord/Message/Sender/DivisionWebhookSender.php similarity index 80% rename from app/Discord/Message/Sender/Sender.php rename to app/Discord/Message/Sender/DivisionWebhookSender.php index 949a18e7..bd372a17 100644 --- a/app/Discord/Message/Sender/Sender.php +++ b/app/Discord/Message/Sender/DivisionWebhookSender.php @@ -2,17 +2,17 @@ namespace App\Discord\Message\Sender; -use App\Discord\DiscordInterface; -use App\Models\DiscordNotification; +use App\Discord\DiscordWebhookInterface; +use App\Models\DivisionDiscordNotification; use DB; use Exception; -class Sender +class DivisionWebhookSender { private readonly array $generators; - private readonly DiscordInterface $discord; + private readonly DiscordWebhookInterface $discord; - public function __construct(array $generators, DiscordInterface $discord) + public function __construct(array $generators, DiscordWebhookInterface $discord) { $this->generators = $generators; $this->discord = $discord; @@ -33,7 +33,7 @@ public function sendDiscordMessages(): void // Associate it and log it DB::transaction(function () use ($message) { - $notification = DiscordNotification::create( + $notification = DivisionDiscordNotification::create( [ 'division_discord_webhook_id' => $message->destination()->id(), 'content' => $message->content(), diff --git a/app/Filament/Resources/DivisionDiscordWebhookResource.php b/app/Filament/Resources/DivisionDiscordWebhookResource.php new file mode 100644 index 00000000..ece3d649 --- /dev/null +++ b/app/Filament/Resources/DivisionDiscordWebhookResource.php @@ -0,0 +1,77 @@ +schema([ + TextInput::make('description') + ->label(__('Description')) + ->required() + ->maxLength(255), + TextInput::make('url') + ->url() + ->required() + ->maxLength( + 500 + ) + ]); + } + + public static function table(Table $table): Table + { + return $table + ->columns([ + TextColumn::make('id') + ->label(__('id')), + TextColumn::make('description') + ->label(__('description')) + ->searchable(), + TagsColumn::make('firs') + ->getStateUsing(fn (DivisionDiscordWebhook $record) => $record->flightInformationRegions->map(fn (FlightInformationRegion $fir) => $fir->identifierName)->toArray()) + ->label(__('FIRs')), + TextColumn::make('created_at') + ->label(__('Created At')), + ]) + ->actions([ + Tables\Actions\EditAction::make(), + ]); + } + + public static function getRelations(): array + { + return [ + FlightinformationregionsRelationManager::class, + ]; + } + + public static function getPages(): array + { + return [ + 'index' => Pages\ListDivisionDiscordWebhooks::route('/'), + 'create' => Pages\CreateDivisionDiscordWebhook::route('/create'), + 'edit' => Pages\EditDivisionDiscordWebhook::route('/{record}/edit'), + ]; + } +} diff --git a/app/Filament/Resources/DivisionDiscordWebhookResource/Pages/CreateDivisionDiscordWebhook.php b/app/Filament/Resources/DivisionDiscordWebhookResource/Pages/CreateDivisionDiscordWebhook.php new file mode 100644 index 00000000..ea8db2c7 --- /dev/null +++ b/app/Filament/Resources/DivisionDiscordWebhookResource/Pages/CreateDivisionDiscordWebhook.php @@ -0,0 +1,11 @@ +columns([ + Tables\Columns\TextColumn::make('fir') + ->label(__('FIR')) + ->formatStateUsing(fn (FlightInformationRegion $record) => $record->identifierName), + Tables\Columns\TextColumn::make('tag') + ->label(__('Mention Tag')), + ]) + ->headerActions([ + AttachAction::make('attach-fir') + ->form(fn (AttachAction $action) => [ + $action->getRecordSelect(), + TextInput::make('tag') + ->label(__('Discord Tag')) + ->helperText(__('A Discord tag for a person / group / role that should be mentioned in the post.')) + ->maxLength(255) + ]), + ]) + ->actions([ + DetachAction::make(), + ]); + } +} diff --git a/app/Filament/Resources/FlightInformationRegionResource/RelationManagers/DiscordTagsRelationManager.php b/app/Filament/Resources/FlightInformationRegionResource/RelationManagers/DiscordTagsRelationManager.php index 052f56b2..1a3aef7f 100644 --- a/app/Filament/Resources/FlightInformationRegionResource/RelationManagers/DiscordTagsRelationManager.php +++ b/app/Filament/Resources/FlightInformationRegionResource/RelationManagers/DiscordTagsRelationManager.php @@ -2,12 +2,15 @@ namespace App\Filament\Resources\FlightInformationRegionResource\RelationManagers; +use App\Models\DiscordTag; use Filament\Forms; use Filament\Tables; use Filament\Resources\Form; use Filament\Resources\Table; use Illuminate\Database\Eloquent\Model; use Filament\Resources\RelationManagers\RelationManager; +use Filament\Tables\Actions\AttachAction; +use Illuminate\Database\Eloquent\Builder; class DiscordTagsRelationManager extends RelationManager { @@ -42,7 +45,20 @@ public static function table(Table $table): Table ]) ->headerActions([ Tables\Actions\CreateAction::make(), - Tables\Actions\AttachAction::make(), + Tables\Actions\AttachAction::make() + ->form(fn (AttachAction $action) => [ + $action->getRecordSelect() + ->getSearchResultsUsing(fn (string $query, DiscordTagsRelationManager $livewire) => DiscordTag::whereDoesntHave('flightInformationRegions', function (Builder $fir) use ($livewire) { + $fir->where('flight_information_regions.id', $livewire->ownerRecord->id); + }) + ->where(function (Builder $subquery) use ($query) { + $subquery->where('tag', 'like', '%' . $query . '%') + ->orWhere('description', 'like', '%' . $query . '%'); + }) + ->get() + ->mapWithKeys(fn (DiscordTag $tag) => [$tag->id => sprintf('%s (%s)', $tag->description, $tag->tag)]) + ->toArray()) + ]), ]) ->actions([ Tables\Actions\EditAction::make(), diff --git a/app/Filament/Resources/FlowMeasureResource/Pages/CreateFlowMeasure.php b/app/Filament/Resources/FlowMeasureResource/Pages/CreateFlowMeasure.php index f89c8b3c..edcb9f4a 100644 --- a/app/Filament/Resources/FlowMeasureResource/Pages/CreateFlowMeasure.php +++ b/app/Filament/Resources/FlowMeasureResource/Pages/CreateFlowMeasure.php @@ -118,6 +118,8 @@ protected function mutateFormDataBeforeCreate(array $data): array } $data['identifier'] = FlowMeasureIdentifierGenerator::generateIdentifier($startTime, $fir); + $data['canonical_identifier'] = FlowMeasureIdentifierGenerator::canonicalIdentifier($data['identifier']); + $data['revision_number'] = FlowMeasureIdentifierGenerator::timesRevised($data['identifier']); $data['user_id'] = auth()->id(); switch ($data['type']) { diff --git a/app/Filament/Resources/FlowMeasureResource/Pages/EditFlowMeasure.php b/app/Filament/Resources/FlowMeasureResource/Pages/EditFlowMeasure.php index 53561c83..2aff443e 100644 --- a/app/Filament/Resources/FlowMeasureResource/Pages/EditFlowMeasure.php +++ b/app/Filament/Resources/FlowMeasureResource/Pages/EditFlowMeasure.php @@ -150,6 +150,8 @@ protected function handleRecordUpdate(Model $record, array $data): Model $newFlowMeasure = $record->replicate(); $newFlowMeasure->fill($data); $newFlowMeasure->identifier = FlowMeasureIdentifierGenerator::generateIdentifier($newFlowMeasure->start_time, $newFlowMeasure->flightInformationRegion); + $newFlowMeasure->canonical_identifier = $newFlowMeasure->identifier; + $newFlowMeasure->revision_number = FlowMeasureIdentifierGenerator::timesRevised($newFlowMeasure->identifier); $newFlowMeasure->push(); /* @@ -164,7 +166,9 @@ protected function handleRecordUpdate(Model $record, array $data): Model return $newFlowMeasure; } + $record->identifier = FlowMeasureIdentifierGenerator::generateRevisedIdentifier($record); + $record->revision_number = FlowMeasureIdentifierGenerator::timesRevised($record->identifier); $record->update($data); diff --git a/app/Helpers/FlowMeasureIdentifierGenerator.php b/app/Helpers/FlowMeasureIdentifierGenerator.php index b0ed05fb..b82fdd66 100644 --- a/app/Helpers/FlowMeasureIdentifierGenerator.php +++ b/app/Helpers/FlowMeasureIdentifierGenerator.php @@ -37,14 +37,35 @@ public static function generateIdentifier( ); } - public static function timesRevised(FlowMeasure $flowMeasure) + public static function timesRevised(FlowMeasure|string $flowMeasure) { + if ($flowMeasure instanceof FlowMeasure) { + return self::timesRevised($flowMeasure->identifier); + } + $identifierParts = []; - preg_match(self::IDENTIFIER_REGEX, $flowMeasure->identifier, $identifierParts); + preg_match(self::IDENTIFIER_REGEX, $flowMeasure, $identifierParts); return isset($identifierParts[5]) ? ((int)$identifierParts[5]) - 1 : 0; } + public static function canonicalIdentifier(FlowMeasure|string $flowMeasure): string + { + if ($flowMeasure instanceof FlowMeasure) { + return self::canonicalIdentifier($flowMeasure->identifier); + } + + $identifierParts = []; + preg_match(self::IDENTIFIER_REGEX, $flowMeasure, $identifierParts); + + return sprintf( + '%s%s%s', + $identifierParts[1], + $identifierParts[2], + $identifierParts[3] + ); + } + private static function designator(Carbon $startTime, FlightInformationRegion $flightInformationRegion): string { $flowMeasuresToday = FlowMeasure::where('start_time', '>=', $startTime->copy()->startOfDay()) diff --git a/app/Jobs/SendDiscordNotifications.php b/app/Jobs/SendDiscordNotifications.php index 9a72a357..ca64d390 100644 --- a/app/Jobs/SendDiscordNotifications.php +++ b/app/Jobs/SendDiscordNotifications.php @@ -2,7 +2,8 @@ namespace App\Jobs; -use App\Discord\Message\Sender\Sender; +use App\Discord\FlowMeasure\Generator\EcfmpFlowMeasureMessageGenerator; +use App\Discord\Message\Sender\DivisionWebhookSender; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldBeUnique; use Illuminate\Contracts\Queue\ShouldQueue; @@ -18,11 +19,13 @@ class SendDiscordNotifications implements ShouldQueue, ShouldBeUnique use Queueable; use SerializesModels; - private readonly Sender $sender; + private readonly DivisionWebhookSender $sender; + private readonly EcfmpFlowMeasureMessageGenerator $generator; - public function __construct(Sender $sender) + public function __construct(DivisionWebhookSender $sender, EcfmpFlowMeasureMessageGenerator $generator) { $this->sender = $sender; + $this->generator = $generator; } public function handle(): void @@ -34,6 +37,7 @@ public function handle(): void Log::info('Sending discord notifications'); $this->sender->sendDiscordMessages(); + $this->generator->generateAndSend(); Log::info('Discord notification sending complete'); } } diff --git a/app/Models/DiscordNotification.php b/app/Models/DiscordNotification.php index ac1bb7d9..8328a434 100644 --- a/app/Models/DiscordNotification.php +++ b/app/Models/DiscordNotification.php @@ -2,11 +2,8 @@ namespace App\Models; -use App\Enums\DiscordNotificationType; -use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; -use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\BelongsToMany; class DiscordNotification extends Model @@ -16,45 +13,13 @@ class DiscordNotification extends Model public const UPDATED_AT = null; protected $fillable = [ - 'division_discord_webhook_id', - 'content', - 'embeds', - ]; - - protected $casts = [ - 'type' => DiscordNotificationType::class, - 'embeds' => 'array', + 'remote_id', ]; public function flowMeasure(): BelongsToMany { return $this->belongsToMany(FlowMeasure::class) - ->withPivot(['type', 'notified_as']) + ->withPivot(['notified_as', 'discord_notification_type_id']) ->withTimestamps(); } - - public function scopeType(Builder $query, DiscordNotificationType $type): Builder - { - return $query->where('type', $type); - } - - public function scopeTypes(Builder $query, array $types): Builder - { - return $query->whereIn('type', $types); - } - - public function divisionDiscordWebhook(): BelongsTo - { - return $this->belongsTo(DivisionDiscordWebhook::class); - } - - public function scopeIsEcfmp(Builder $query): Builder - { - return $query->whereNull('division_discord_webhook_id'); - } - - public function scopeIsDivision(Builder $query): Builder - { - return $query->whereNotNull('division_discord_webhook_id'); - } } diff --git a/app/Models/DivisionDiscordNotification.php b/app/Models/DivisionDiscordNotification.php new file mode 100644 index 00000000..acc231c7 --- /dev/null +++ b/app/Models/DivisionDiscordNotification.php @@ -0,0 +1,60 @@ + DiscordNotificationType::class, + 'embeds' => 'array', + ]; + + public function flowMeasure(): BelongsToMany + { + return $this->belongsToMany(FlowMeasure::class) + ->withPivot(['type', 'notified_as']) + ->withTimestamps(); + } + + public function scopeType(Builder $query, DiscordNotificationType $type): Builder + { + return $query->where('type', $type); + } + + public function scopeTypes(Builder $query, array $types): Builder + { + return $query->whereIn('type', $types); + } + + public function divisionDiscordWebhook(): BelongsTo + { + return $this->belongsTo(DivisionDiscordWebhook::class); + } + + public function scopeIsEcfmp(Builder $query): Builder + { + return $query->whereNull('division_discord_webhook_id'); + } + + public function scopeIsDivision(Builder $query): Builder + { + return $query->whereNotNull('division_discord_webhook_id'); + } +} diff --git a/app/Models/FlowMeasure.php b/app/Models/FlowMeasure.php index 04ba2676..35d89198 100644 --- a/app/Models/FlowMeasure.php +++ b/app/Models/FlowMeasure.php @@ -6,7 +6,6 @@ use App\Enums\FilterType; use App\Enums\FlowMeasureStatus; use App\Enums\FlowMeasureType; -use App\Helpers\FlowMeasureIdentifierGenerator; use Carbon\Carbon; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Casts\Attribute; @@ -23,6 +22,8 @@ class FlowMeasure extends Model protected $fillable = [ 'identifier', + 'canonical_identifier', + 'revision_number', 'user_id', 'flight_information_region_id', 'event_id', @@ -36,6 +37,7 @@ class FlowMeasure extends Model ]; protected $casts = [ + 'revision_number' => 'integer', 'mandatory_route' => 'array', 'filters' => 'array', 'start_time' => 'datetime', @@ -116,6 +118,13 @@ public function scopeFlightInformationRegion( return $query->where('flight_information_region_id', $flightInformationRegion->id); } + public function divisionDiscordNotifications(): BelongsToMany + { + return $this->belongsToMany(DivisionDiscordNotification::class) + ->withPivot(['discord_notification_type_id', 'notified_as']) + ->withTimestamps(); + } + public function discordNotifications(): BelongsToMany { return $this->belongsToMany(DiscordNotification::class) @@ -123,29 +132,46 @@ public function discordNotifications(): BelongsToMany ->withTimestamps(); } - public function notifiedDiscordNotifications(): BelongsToMany + public function discordNotificationsOfType(array $types): BelongsToMany { - return $this->notificationsOfType([DiscordNotificationTypeEnum::FLOW_MEASURE_NOTIFIED]); + return $this->discordNotifications() + ->wherePivotIn( + 'discord_notification_type_id', + DiscordNotificationType::whereIn( + 'type', + $types + )->pluck('id') + ); + } + + public function notifiedDivisionNotifications(): BelongsToMany + { + return $this->divisionNotificationsOfType([DiscordNotificationTypeEnum::FLOW_MEASURE_NOTIFIED]); } - public function activatedDiscordNotifications(): BelongsToMany + public function notifiedEcfmpNotifications(): BelongsToMany { - return $this->notificationsOfType([DiscordNotificationTypeEnum::FLOW_MEASURE_ACTIVATED]); + return $this->discordNotificationsOfType([DiscordNotificationTypeEnum::FLOW_MEASURE_NOTIFIED]); } - public function withdrawnDiscordNotifications(): BelongsToMany + public function activatedDivisionNotifications(): BelongsToMany { - return $this->notificationsOfType([DiscordNotificationTypeEnum::FLOW_MEASURE_WITHDRAWN]); + return $this->divisionNotificationsOfType([DiscordNotificationTypeEnum::FLOW_MEASURE_ACTIVATED]); } - public function expiredDiscordNotifications(): BelongsToMany + public function withdrawnDivisionNotifications(): BelongsToMany { - return $this->notificationsOfType([DiscordNotificationTypeEnum::FLOW_MEASURE_EXPIRED]); + return $this->divisionNotificationsOfType([DiscordNotificationTypeEnum::FLOW_MEASURE_WITHDRAWN]); } - public function withdrawnAndExpiredDiscordNotifications(): BelongsToMany + public function expiredDivisionNotifications(): BelongsToMany { - return $this->notificationsOfType( + return $this->divisionNotificationsOfType([DiscordNotificationTypeEnum::FLOW_MEASURE_EXPIRED]); + } + + public function withdrawnAndExpiredDivisionNotifications(): BelongsToMany + { + return $this->divisionNotificationsOfType( [ DiscordNotificationTypeEnum::FLOW_MEASURE_WITHDRAWN, DiscordNotificationTypeEnum::FLOW_MEASURE_EXPIRED, @@ -153,9 +179,9 @@ public function withdrawnAndExpiredDiscordNotifications(): BelongsToMany ); } - public function activatedAndNotifiedNotifications(): BelongsToMany + public function activatedAndNotifiedDivisionNotifications(): BelongsToMany { - return $this->notificationsOfType( + return $this->divisionNotificationsOfType( [ DiscordNotificationTypeEnum::FLOW_MEASURE_NOTIFIED, DiscordNotificationTypeEnum::FLOW_MEASURE_ACTIVATED, @@ -163,9 +189,51 @@ public function activatedAndNotifiedNotifications(): BelongsToMany ); } - private function notificationsOfType(array $types): BelongsToMany + public function scopeWithoutEcfmpNotificationOfTypeForIdentifier( + Builder $query, + DiscordNotificationTypeEnum $type + ): Builder { + return $query->whereDoesntHave('discordNotifications', function (Builder $query) use ($type) { + $query->where('discord_notification_type_id', DiscordNotificationType::where('type', $type)->firstOrFail()->id) + ->whereRaw('`notified_as` = `identifier`'); + }); + } + + public function scopeWithoutEcfmpNotificationOfType( + Builder $query, + DiscordNotificationTypeEnum $type + ): Builder { + return $this->scopeWithoutEcfmpNotificationOfTypes($query, [$type]); + } + + public function scopeWithoutEcfmpNotificationOfTypes( + Builder $query, + array $types + ): Builder { + return $query->whereDoesntHave('discordNotifications', function (Builder $query) use ($types) { + $query->whereIn('discord_notification_type_id', DiscordNotificationType::whereIn('type', $types)->pluck('id')); + }); + } + + public function scopeWithEcfmpNotificationOfType( + Builder $query, + DiscordNotificationTypeEnum $type + ): Builder { + return $this->scopeWithEcfmpNotificationOfTypes($query, [$type]); + } + + public function scopeWithEcfmpNotificationOfTypes( + Builder $query, + array $types + ): Builder { + return $query->whereHas('discordNotifications', function (Builder $query) use ($types) { + $query->whereIn('discord_notification_type_id', DiscordNotificationType::whereIn('type', $types)->pluck('id')); + }); + } + + private function divisionNotificationsOfType(array $types): BelongsToMany { - return $this->discordNotifications() + return $this->divisionDiscordNotifications() ->wherePivotIn( 'discord_notification_type_id', DiscordNotificationType::whereIn( @@ -231,12 +299,4 @@ public function status(): Attribute return FlowMeasureStatus::NOTIFIED; }); } - - public function reissueIdentifier(bool $save = true): void - { - $this->identifier = FlowMeasureIdentifierGenerator::generateRevisedIdentifier($this); - if ($save) { - $this->save(); - } - } } diff --git a/app/Policies/DivisionDiscordWebhookPolicy.php b/app/Policies/DivisionDiscordWebhookPolicy.php new file mode 100644 index 00000000..05b7189a --- /dev/null +++ b/app/Policies/DivisionDiscordWebhookPolicy.php @@ -0,0 +1,156 @@ +role->key, [ + RoleKey::SYSTEM, + RoleKey::NMT, + ]); + } + + /** + * Determine whether the user can view the model. + * + * @param \App\Models\User $user + * @param \App\Models\DivisionDiscordWebhook $divisionDiscordWebhook + * @return \Illuminate\Auth\Access\Response|bool + */ + public function view(User $user, DivisionDiscordWebhook $divisionDiscordWebhook) + { + return in_array($user->role->key, [ + RoleKey::SYSTEM, + RoleKey::NMT, + ]); + } + + /** + * Determine whether the user can create models. + * + * @param \App\Models\User $user + * @return \Illuminate\Auth\Access\Response|bool + */ + public function create(User $user) + { + return in_array($user->role->key, [ + RoleKey::SYSTEM, + RoleKey::NMT, + ]); + } + + /** + * Determine whether the user can update the model. + * + * @param \App\Models\User $user + * @param \App\Models\DivisionDiscordWebhook $divisionDiscordWebhook + * @return \Illuminate\Auth\Access\Response|bool + */ + public function update(User $user, DivisionDiscordWebhook $divisionDiscordWebhook) + { + return in_array($user->role->key, [ + RoleKey::SYSTEM, + RoleKey::NMT, + ]); + } + + /** + * Determine whether the user can delete the model. + * + * @param \App\Models\User $user + * @param \App\Models\DivisionDiscordWebhook $divisionDiscordWebhook + * @return \Illuminate\Auth\Access\Response|bool + */ + public function delete(User $user, DivisionDiscordWebhook $divisionDiscordWebhook) + { + return in_array($user->role->key, [ + RoleKey::SYSTEM, + RoleKey::NMT, + ]); + } + + /** + * Determine whether the user can restore the model. + * + * @param \App\Models\User $user + * @param \App\Models\DivisionDiscordWebhook $divisionDiscordWebhook + * @return \Illuminate\Auth\Access\Response|bool + */ + public function restore(User $user, DivisionDiscordWebhook $divisionDiscordWebhook) + { + return in_array($user->role->key, [ + RoleKey::SYSTEM, + RoleKey::NMT, + ]); + } + + /** + * Determine whether the user can permanently delete the model. + * + * @param \App\Models\User $user + * @param \App\Models\DivisionDiscordWebhook $divisionDiscordWebhook + * @return \Illuminate\Auth\Access\Response|bool + */ + public function forceDelete(User $user, DivisionDiscordWebhook $divisionDiscordWebhook) + { + return in_array($user->role->key, [ + RoleKey::SYSTEM, + RoleKey::NMT, + ]); + } + + public function deleteAny() + { + return false; + } + + public function detachAny() + { + return false; + } + + /** + * Determine whether the user can restore the model. + * + * @param \App\Models\User $user + * @param \App\Models\DivisionDiscordWebhook $divisionDiscordWebhook + * @return \Illuminate\Auth\Access\Response|bool + */ + public function attach(User $user, DivisionDiscordWebhook $divisionDiscordWebhook) + { + return in_array($user->role->key, [ + RoleKey::SYSTEM, + RoleKey::NMT, + ]); + } + + /** + * Determine whether the user can restore the model. + * + * @param \App\Models\User $user + * @param \App\Models\DivisionDiscordWebhook $divisionDiscordWebhook + * @return \Illuminate\Auth\Access\Response|bool + */ + public function detach(User $user, DivisionDiscordWebhook $divisionDiscordWebhook) + { + return in_array($user->role->key, [ + RoleKey::SYSTEM, + RoleKey::NMT, + ]); + } +} diff --git a/app/Providers/AuthServiceProvider.php b/app/Providers/AuthServiceProvider.php index 4397dfae..99316ce2 100644 --- a/app/Providers/AuthServiceProvider.php +++ b/app/Providers/AuthServiceProvider.php @@ -2,7 +2,9 @@ namespace App\Providers; +use App\Models\DivisionDiscordWebhook; use App\Policies\ActivityPolicy; +use App\Policies\DivisionDiscordWebhookPolicy; use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider; use Spatie\Activitylog\Models\Activity; @@ -15,6 +17,7 @@ class AuthServiceProvider extends ServiceProvider */ protected $policies = [ Activity::class => ActivityPolicy::class, + DivisionDiscordWebhook::class => DivisionDiscordWebhookPolicy::class, ]; /** diff --git a/app/Providers/DiscordServiceProvider.php b/app/Providers/DiscordServiceProvider.php index 01c9f943..5a45d58e 100644 --- a/app/Providers/DiscordServiceProvider.php +++ b/app/Providers/DiscordServiceProvider.php @@ -2,19 +2,25 @@ namespace App\Providers; -use App\Discord\DiscordInterface; -use App\Discord\DiscordMessageSender; +use App\Discord\Client\ClientFactory; +use App\Discord\Client\ClientFactoryInterface; +use App\Discord\DiscordServiceInterface; +use App\Discord\DiscordServiceMessageSender; +use App\Discord\DiscordWebhookInterface; +use App\Discord\DiscordWebhookSender; +use App\Discord\FlowMeasure\Generator\EcfmpFlowMeasureMessageGenerator; use App\Discord\FlowMeasure\Message\FlowMeasureMessageFactory; use App\Discord\FlowMeasure\Message\MessageGenerator; use App\Discord\FlowMeasure\Message\MessageGeneratorInterface; -use App\Discord\FlowMeasure\Provider\MessageProvider; +use App\Discord\FlowMeasure\Provider\DivisionWebhookMessageProvider; +use App\Discord\FlowMeasure\Sender\EcfmpFlowMeasureSender; use App\Discord\FlowMeasure\Webhook\Filter\ActivatedWebhookFilter; use App\Discord\FlowMeasure\Webhook\Filter\ExpiredWebhookFilter; use App\Discord\FlowMeasure\Webhook\Filter\FilterInterface; use App\Discord\FlowMeasure\Webhook\Filter\NotifiedWebhookFilter; use App\Discord\FlowMeasure\Webhook\Filter\WithdrawnWebhookFilter; use App\Discord\FlowMeasure\Webhook\WebhookMapper; -use App\Discord\Message\Sender\Sender; +use App\Discord\Message\Sender\DivisionWebhookSender; use App\Discord\Webhook\EcfmpWebhook; use App\Repository\FlowMeasureNotification\ActiveRepository; use App\Repository\FlowMeasureNotification\ExpiredRepository; @@ -32,16 +38,38 @@ class DiscordServiceProvider extends ServiceProvider ExpiredRepository::class => ExpiredWebhookFilter::class, ]; - public function register() + public function register(): void { - $this->app->singleton(DiscordInterface::class, function () { - return new DiscordMessageSender(); + $this->app->singleton(DiscordWebhookInterface::class, function () { + return new DiscordWebhookSender(); }); $this->app->singleton(EcfmpWebhook::class); - $this->app->singleton(Sender::class, fn () => new Sender( - $this->flowMeasureMessageProviders(), - $this->app->make(DiscordInterface::class) - )); + $this->app->singleton( + DivisionWebhookSender::class, + fn () => new DivisionWebhookSender( + $this->flowMeasureMessageProviders(), + $this->app->make(DiscordWebhookInterface::class) + ) + ); + + $this->app->singleton( + DiscordServiceInterface::class, + fn () => $this->app->make(DiscordServiceMessageSender::class) + ); + $this->app->singleton(ClientFactoryInterface::class, ClientFactory::class); + $this->app->singleton(ClientFactory::class); + $this->app->singleton( + EcfmpFlowMeasureMessageGenerator::class, + function () { + return new EcfmpFlowMeasureMessageGenerator( + $this->app->make(EcfmpFlowMeasureSender::class), + array_map( + fn (string $repository) => $this->app->make($repository), + array_keys(self::FLOW_MEASURE_MESSAGE_REPOSITORIES) + ) + ); + } + ); } private function flowMeasureMessageProviders(): array @@ -63,7 +91,7 @@ private function makeMessageProvider( FilterInterface $filter ): MessageGeneratorInterface { return new MessageGenerator( - new MessageProvider( + new DivisionWebhookMessageProvider( $repository, $this->app->make( WebhookMapper::class, diff --git a/app/Repository/FlowMeasureNotification/ActiveRepository.php b/app/Repository/FlowMeasureNotification/ActiveRepository.php index 58370ea3..0400ae90 100644 --- a/app/Repository/FlowMeasureNotification/ActiveRepository.php +++ b/app/Repository/FlowMeasureNotification/ActiveRepository.php @@ -4,13 +4,46 @@ use App\Enums\DiscordNotificationType; use App\Models\FlowMeasure; +use Illuminate\Database\Query\Builder; use Illuminate\Support\Collection; class ActiveRepository implements RepositoryInterface { + public function flowMeasuresToBeSentToEcfmp(): Collection + { + return FlowMeasure::active() + ->withoutEcfmpNotificationOfTypeForIdentifier($this->notificationType()) + ->select('flow_measures.*') + ->selectSub( + function (Builder $query) { + $query->selectRaw('COUNT(*)') + ->from('discord_notification_flow_measure', 'previous_notifications_for_identifier') + ->whereRaw('previous_notifications_for_identifier.flow_measure_id = flow_measures.id') + ->whereRaw('previous_notifications_for_identifier.notified_as = flow_measures.identifier'); + }, + 'count_previous_for_identifier' + ) + ->selectSub( + function (Builder $query) { + $query->selectRaw('COUNT(*)') + ->from('discord_notification_flow_measure', 'previous_notifications_for_other_identifier') + ->whereRaw('previous_notifications_for_other_identifier.flow_measure_id = flow_measures.id') + ->whereRaw('previous_notifications_for_other_identifier.notified_as <> flow_measures.identifier'); + }, + 'count_previous_other_identifiers' + ) + ->get() + ->map( + fn (FlowMeasure $measure) => new FlowMeasureForNotification( + $measure, + $measure->count_previous_for_identifier === 0 && $measure->count_previous_other_identifiers !== 0 + ) + ); + } + public function flowMeasuresForNotification(): Collection { - return FlowMeasure::with('discordNotifications') + return FlowMeasure::with('divisionDiscordNotifications') ->active() ->get(); } diff --git a/app/Repository/FlowMeasureNotification/ExpiredRepository.php b/app/Repository/FlowMeasureNotification/ExpiredRepository.php index 67a88cc2..6f9da9b4 100644 --- a/app/Repository/FlowMeasureNotification/ExpiredRepository.php +++ b/app/Repository/FlowMeasureNotification/ExpiredRepository.php @@ -3,14 +3,44 @@ namespace App\Repository\FlowMeasureNotification; use App\Enums\DiscordNotificationType; +use App\Models\DiscordNotification; use App\Models\FlowMeasure; +use Carbon\Carbon; use Illuminate\Support\Collection; class ExpiredRepository implements RepositoryInterface { + public function flowMeasuresToBeSentToEcfmp(): Collection + { + $webhooksSentInLastTwoHours = DiscordNotification::where('created_at', '>=', Carbon::now()->subHours(2)) + ->count(); + + if ($webhooksSentInLastTwoHours > 5) { + return collect(); + } + + return FlowMeasure::expiredRecently() + ->where('revision_number', '<', 2) + ->withEcfmpNotificationOfTypes([ + DiscordNotificationType::FLOW_MEASURE_ACTIVATED, + DiscordNotificationType::FLOW_MEASURE_NOTIFIED, + ]) + ->withoutEcfmpNotificationOfTypes([ + DiscordNotificationType::FLOW_MEASURE_EXPIRED, + DiscordNotificationType::FLOW_MEASURE_WITHDRAWN, + ]) + ->get() + ->map( + fn (FlowMeasure $measure) => new FlowMeasureForNotification( + $measure, + false + ) + ); + } + public function flowMeasuresForNotification(): Collection { - return FlowMeasure::with('discordNotifications') + return FlowMeasure::with('divisionDiscordNotifications') ->expiredRecently() ->get(); } diff --git a/app/Repository/FlowMeasureNotification/FlowMeasureForNotification.php b/app/Repository/FlowMeasureNotification/FlowMeasureForNotification.php new file mode 100644 index 00000000..a466c108 --- /dev/null +++ b/app/Repository/FlowMeasureNotification/FlowMeasureForNotification.php @@ -0,0 +1,14 @@ +addDay()) + ->where('start_time', '>', Carbon::now()) + ->withoutEcfmpNotificationOfTypeForIdentifier($this->notificationType()) + ->withoutEcfmpNotificationOfType(DiscordNotificationType::FLOW_MEASURE_ACTIVATED) + ->select('flow_measures.*') + ->selectSub( + function (Builder $query) { + $query->selectRaw('COUNT(*)') + ->from('discord_notification_flow_measure', 'previous_notifications_for_identifier') + ->whereRaw('previous_notifications_for_identifier.flow_measure_id = flow_measures.id') + ->whereRaw('previous_notifications_for_identifier.notified_as = flow_measures.identifier'); + }, + 'count_previous_for_identifier' + ) + ->selectSub( + function (Builder $query) { + $query->selectRaw('COUNT(*)') + ->from('discord_notification_flow_measure', 'previous_notifications_for_other_identifier') + ->whereRaw('previous_notifications_for_other_identifier.flow_measure_id = flow_measures.id') + ->whereRaw('previous_notifications_for_other_identifier.notified_as <> flow_measures.identifier'); + }, + 'count_previous_other_identifiers' + ) + ->get() + ->map( + fn (FlowMeasure $measure) => new FlowMeasureForNotification( + $measure, + $measure->count_previous_for_identifier === 0 && $measure->count_previous_other_identifiers !== 0 + ) + ); + } + public function flowMeasuresForNotification(): Collection { - return FlowMeasure::with('discordNotifications') + return FlowMeasure::with('divisionDiscordNotifications') ->where('start_time', '<', Carbon::now()->addDay()) ->where('start_time', '>', Carbon::now()) ->get(); diff --git a/app/Repository/FlowMeasureNotification/RepositoryInterface.php b/app/Repository/FlowMeasureNotification/RepositoryInterface.php index 6f57c725..230e27a4 100644 --- a/app/Repository/FlowMeasureNotification/RepositoryInterface.php +++ b/app/Repository/FlowMeasureNotification/RepositoryInterface.php @@ -7,6 +7,11 @@ interface RepositoryInterface { + /** + * Returns the flow measures that need to be sent to ECFMP. + */ + public function flowMeasuresToBeSentToEcfmp(): Collection; + /** * Get all the flow measures for notification. */ diff --git a/app/Repository/FlowMeasureNotification/WithdrawnRepository.php b/app/Repository/FlowMeasureNotification/WithdrawnRepository.php index d7da842a..aaeaad31 100644 --- a/app/Repository/FlowMeasureNotification/WithdrawnRepository.php +++ b/app/Repository/FlowMeasureNotification/WithdrawnRepository.php @@ -10,6 +10,20 @@ class WithdrawnRepository implements RepositoryInterface { + public function flowMeasuresToBeSentToEcfmp(): Collection + { + return $this->baseQueryForEcfmp()->notified() + ->union($this->baseQueryForEcfmp()->active()) + ->orderBy('id') + ->get() + ->map( + fn (FlowMeasure $measure) => new FlowMeasureForNotification( + $measure, + false + ) + ); + } + public function flowMeasuresForNotification(): Collection { return $this->baseQuery()->notified() @@ -18,9 +32,22 @@ public function flowMeasuresForNotification(): Collection ->get(); } + public function baseQueryForEcfmp(): Builder + { + return $this->baseQuery() + ->WithEcfmpNotificationOfTypes([ + DiscordNotificationType::FLOW_MEASURE_ACTIVATED, + DiscordNotificationType::FLOW_MEASURE_NOTIFIED, + ]) + ->withoutEcfmpNotificationOfTypes([ + DiscordNotificationType::FLOW_MEASURE_EXPIRED, + DiscordNotificationType::FLOW_MEASURE_WITHDRAWN, + ]); + } + private function baseQuery(): Builder { - return FlowMeasure::with('discordNotifications') + return FlowMeasure::with('divisionDiscordNotifications') ->onlyTrashed() ->where('deleted_at', '>', Carbon::now()->subHour()); } diff --git a/composer.json b/composer.json index badf375d..3749f7f8 100644 --- a/composer.json +++ b/composer.json @@ -8,10 +8,13 @@ ], "license": "MIT", "require": { - "php": "^8.1", + "php": "^8.2", + "ext-grpc": "*", "ext-json": "*", "doctrine/dbal": "^3.3", "filament/filament": "^2.14", + "google/protobuf": "^3.17", + "grpc/grpc": "^1.38", "guzzlehttp/guzzle": "^7.2", "laravel/framework": "^10.5", "laravel/socialite": "^5.5", @@ -28,7 +31,7 @@ "barryvdh/laravel-debugbar": "^3.6", "barryvdh/laravel-ide-helper": "^2.12", "fakerphp/faker": "^1.9.1", - "laravel-lang/lang": "^12.0", + "laravel-lang/lang": "^13.2", "laravel-lang/publisher": "^14.0", "laravel/pint": "^1.0.0", "laravel/sail": "^1.0.1", @@ -45,7 +48,9 @@ "App\\": "app/", "Database\\Factories\\": "database/factories/", "Database\\Seeders\\": "database/seeders/", - "SocialiteProviders\\VatsimConnect\\": "SocialiteProviders/src/VatsimConnect/" + "SocialiteProviders\\VatsimConnect\\": "SocialiteProviders/src/VatsimConnect/", + "Ecfmp_discord\\": "protobuf/discord/gen/pb-php/Ecfmp_discord/", + "GPBMetadata\\": "protobuf/discord/gen/pb-php/GPBMetadata/" } }, "autoload-dev": { diff --git a/composer.lock b/composer.lock index d352bbb2..7d0f5e5f 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "2918355381a18c24fc6e9abc77b2e117", + "content-hash": "ea2754f9df7a7351175e772ec656f410", "packages": [ { "name": "akaunting/laravel-money", @@ -348,16 +348,16 @@ }, { "name": "composer/semver", - "version": "3.3.2", + "version": "3.4.0", "source": { "type": "git", "url": "https://github.com/composer/semver.git", - "reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9" + "reference": "35e8d0af4486141bc745f23a29cc2091eb624a32" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/3953f23262f2bff1919fc82183ad9acb13ff62c9", - "reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9", + "url": "https://api.github.com/repos/composer/semver/zipball/35e8d0af4486141bc745f23a29cc2091eb624a32", + "reference": "35e8d0af4486141bc745f23a29cc2091eb624a32", "shasum": "" }, "require": { @@ -407,9 +407,9 @@ "versioning" ], "support": { - "irc": "irc://irc.freenode.org/composer", + "irc": "ircs://irc.libera.chat:6697/composer", "issues": "https://github.com/composer/semver/issues", - "source": "https://github.com/composer/semver/tree/3.3.2" + "source": "https://github.com/composer/semver/tree/3.4.0" }, "funding": [ { @@ -425,7 +425,7 @@ "type": "tidelift" } ], - "time": "2022-04-01T19:23:25+00:00" + "time": "2023-08-31T09:50:34+00:00" }, { "name": "danharrin/date-format-converter", @@ -813,25 +813,29 @@ }, { "name": "doctrine/deprecations", - "version": "v1.0.0", + "version": "v1.1.1", "source": { "type": "git", "url": "https://github.com/doctrine/deprecations.git", - "reference": "0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de" + "reference": "612a3ee5ab0d5dd97b7cf3874a6efe24325efac3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/deprecations/zipball/0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de", - "reference": "0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/612a3ee5ab0d5dd97b7cf3874a6efe24325efac3", + "reference": "612a3ee5ab0d5dd97b7cf3874a6efe24325efac3", "shasum": "" }, "require": { - "php": "^7.1|^8.0" + "php": "^7.1 || ^8.0" }, "require-dev": { "doctrine/coding-standard": "^9", - "phpunit/phpunit": "^7.5|^8.5|^9.5", - "psr/log": "^1|^2|^3" + "phpstan/phpstan": "1.4.10 || 1.10.15", + "phpstan/phpstan-phpunit": "^1.0", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "psalm/plugin-phpunit": "0.18.4", + "psr/log": "^1 || ^2 || ^3", + "vimeo/psalm": "4.30.0 || 5.12.0" }, "suggest": { "psr/log": "Allows logging deprecations via PSR-3 logger implementation" @@ -850,9 +854,9 @@ "homepage": "https://www.doctrine-project.org/", "support": { "issues": "https://github.com/doctrine/deprecations/issues", - "source": "https://github.com/doctrine/deprecations/tree/v1.0.0" + "source": "https://github.com/doctrine/deprecations/tree/v1.1.1" }, - "time": "2022-05-02T15:47:09+00:00" + "time": "2023-06-03T09:27:29+00:00" }, { "name": "doctrine/event-manager", @@ -947,28 +951,28 @@ }, { "name": "doctrine/inflector", - "version": "2.0.6", + "version": "2.0.8", "source": { "type": "git", "url": "https://github.com/doctrine/inflector.git", - "reference": "d9d313a36c872fd6ee06d9a6cbcf713eaa40f024" + "reference": "f9301a5b2fb1216b2b08f02ba04dc45423db6bff" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/inflector/zipball/d9d313a36c872fd6ee06d9a6cbcf713eaa40f024", - "reference": "d9d313a36c872fd6ee06d9a6cbcf713eaa40f024", + "url": "https://api.github.com/repos/doctrine/inflector/zipball/f9301a5b2fb1216b2b08f02ba04dc45423db6bff", + "reference": "f9301a5b2fb1216b2b08f02ba04dc45423db6bff", "shasum": "" }, "require": { "php": "^7.2 || ^8.0" }, "require-dev": { - "doctrine/coding-standard": "^10", + "doctrine/coding-standard": "^11.0", "phpstan/phpstan": "^1.8", "phpstan/phpstan-phpunit": "^1.1", "phpstan/phpstan-strict-rules": "^1.3", "phpunit/phpunit": "^8.5 || ^9.5", - "vimeo/psalm": "^4.25" + "vimeo/psalm": "^4.25 || ^5.4" }, "type": "library", "autoload": { @@ -1018,7 +1022,7 @@ ], "support": { "issues": "https://github.com/doctrine/inflector/issues", - "source": "https://github.com/doctrine/inflector/tree/2.0.6" + "source": "https://github.com/doctrine/inflector/tree/2.0.8" }, "funding": [ { @@ -1034,7 +1038,7 @@ "type": "tidelift" } ], - "time": "2022-10-20T09:10:12+00:00" + "time": "2023-06-16T13:40:37+00:00" }, { "name": "doctrine/lexer", @@ -1115,16 +1119,16 @@ }, { "name": "dragonmantank/cron-expression", - "version": "v3.3.2", + "version": "v3.3.3", "source": { "type": "git", "url": "https://github.com/dragonmantank/cron-expression.git", - "reference": "782ca5968ab8b954773518e9e49a6f892a34b2a8" + "reference": "adfb1f505deb6384dc8b39804c5065dd3c8c8c0a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/782ca5968ab8b954773518e9e49a6f892a34b2a8", - "reference": "782ca5968ab8b954773518e9e49a6f892a34b2a8", + "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/adfb1f505deb6384dc8b39804c5065dd3c8c8c0a", + "reference": "adfb1f505deb6384dc8b39804c5065dd3c8c8c0a", "shasum": "" }, "require": { @@ -1164,7 +1168,7 @@ ], "support": { "issues": "https://github.com/dragonmantank/cron-expression/issues", - "source": "https://github.com/dragonmantank/cron-expression/tree/v3.3.2" + "source": "https://github.com/dragonmantank/cron-expression/tree/v3.3.3" }, "funding": [ { @@ -1172,7 +1176,7 @@ "type": "github" } ], - "time": "2022-09-10T18:51:20+00:00" + "time": "2023-08-10T19:36:49+00:00" }, { "name": "egulias/email-validator", @@ -1653,6 +1657,50 @@ ], "time": "2022-02-20T15:07:15+00:00" }, + { + "name": "google/protobuf", + "version": "v3.24.4", + "source": { + "type": "git", + "url": "https://github.com/protocolbuffers/protobuf-php.git", + "reference": "672d69e25f71b9364fdf1810eb8a8573defdc404" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/protocolbuffers/protobuf-php/zipball/672d69e25f71b9364fdf1810eb8a8573defdc404", + "reference": "672d69e25f71b9364fdf1810eb8a8573defdc404", + "shasum": "" + }, + "require": { + "php": ">=7.0.0" + }, + "require-dev": { + "phpunit/phpunit": ">=5.0.0" + }, + "suggest": { + "ext-bcmath": "Need to support JSON deserialization" + }, + "type": "library", + "autoload": { + "psr-4": { + "Google\\Protobuf\\": "src/Google/Protobuf", + "GPBMetadata\\Google\\Protobuf\\": "src/GPBMetadata/Google/Protobuf" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "proto library for PHP", + "homepage": "https://developers.google.com/protocol-buffers/", + "keywords": [ + "proto" + ], + "support": { + "source": "https://github.com/protocolbuffers/protobuf-php/tree/v3.24.4" + }, + "time": "2023-10-04T17:22:47+00:00" + }, { "name": "graham-campbell/result-type", "version": "v1.1.1", @@ -1715,24 +1763,68 @@ ], "time": "2023-02-25T20:23:15+00:00" }, + { + "name": "grpc/grpc", + "version": "1.57.0", + "source": { + "type": "git", + "url": "https://github.com/grpc/grpc-php.git", + "reference": "b610c42022ed3a22f831439cb93802f2a4502fdf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/grpc/grpc-php/zipball/b610c42022ed3a22f831439cb93802f2a4502fdf", + "reference": "b610c42022ed3a22f831439cb93802f2a4502fdf", + "shasum": "" + }, + "require": { + "php": ">=7.0.0" + }, + "require-dev": { + "google/auth": "^v1.3.0" + }, + "suggest": { + "ext-protobuf": "For better performance, install the protobuf C extension.", + "google/protobuf": "To get started using grpc quickly, install the native protobuf library." + }, + "type": "library", + "autoload": { + "psr-4": { + "Grpc\\": "src/lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "description": "gRPC library for PHP", + "homepage": "https://grpc.io", + "keywords": [ + "rpc" + ], + "support": { + "source": "https://github.com/grpc/grpc-php/tree/v1.57.0" + }, + "time": "2023-08-14T23:57:54+00:00" + }, { "name": "guzzlehttp/guzzle", - "version": "7.5.0", + "version": "7.8.0", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "b50a2a1251152e43f6a37f0fa053e730a67d25ba" + "reference": "1110f66a6530a40fe7aea0378fe608ee2b2248f9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/b50a2a1251152e43f6a37f0fa053e730a67d25ba", - "reference": "b50a2a1251152e43f6a37f0fa053e730a67d25ba", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/1110f66a6530a40fe7aea0378fe608ee2b2248f9", + "reference": "1110f66a6530a40fe7aea0378fe608ee2b2248f9", "shasum": "" }, "require": { "ext-json": "*", - "guzzlehttp/promises": "^1.5", - "guzzlehttp/psr7": "^1.9 || ^2.4", + "guzzlehttp/promises": "^1.5.3 || ^2.0.1", + "guzzlehttp/psr7": "^1.9.1 || ^2.5.1", "php": "^7.2.5 || ^8.0", "psr/http-client": "^1.0", "symfony/deprecation-contracts": "^2.2 || ^3.0" @@ -1743,7 +1835,8 @@ "require-dev": { "bamarni/composer-bin-plugin": "^1.8.1", "ext-curl": "*", - "php-http/client-integration-tests": "^3.0", + "php-http/client-integration-tests": "dev-master#2c025848417c1135031fdf9c728ee53d0a7ceaee as 3.0.999", + "php-http/message-factory": "^1.1", "phpunit/phpunit": "^8.5.29 || ^9.5.23", "psr/log": "^1.1 || ^2.0 || ^3.0" }, @@ -1757,9 +1850,6 @@ "bamarni-bin": { "bin-links": true, "forward-command": false - }, - "branch-alias": { - "dev-master": "7.5-dev" } }, "autoload": { @@ -1825,7 +1915,7 @@ ], "support": { "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/7.5.0" + "source": "https://github.com/guzzle/guzzle/tree/7.8.0" }, "funding": [ { @@ -1841,20 +1931,20 @@ "type": "tidelift" } ], - "time": "2022-08-28T15:39:27+00:00" + "time": "2023-08-27T10:20:53+00:00" }, { "name": "guzzlehttp/promises", - "version": "1.5.2", + "version": "1.5.3", "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", - "reference": "b94b2807d85443f9719887892882d0329d1e2598" + "reference": "67ab6e18aaa14d753cc148911d273f6e6cb6721e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/b94b2807d85443f9719887892882d0329d1e2598", - "reference": "b94b2807d85443f9719887892882d0329d1e2598", + "url": "https://api.github.com/repos/guzzle/promises/zipball/67ab6e18aaa14d753cc148911d273f6e6cb6721e", + "reference": "67ab6e18aaa14d753cc148911d273f6e6cb6721e", "shasum": "" }, "require": { @@ -1864,11 +1954,6 @@ "symfony/phpunit-bridge": "^4.4 || ^5.1" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.5-dev" - } - }, "autoload": { "files": [ "src/functions_include.php" @@ -1909,7 +1994,7 @@ ], "support": { "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/1.5.2" + "source": "https://github.com/guzzle/promises/tree/1.5.3" }, "funding": [ { @@ -1925,26 +2010,26 @@ "type": "tidelift" } ], - "time": "2022-08-28T14:55:35+00:00" + "time": "2023-05-21T12:31:43+00:00" }, { "name": "guzzlehttp/psr7", - "version": "2.4.4", + "version": "2.6.1", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "3cf1b6d4f0c820a2cf8bcaec39fc698f3443b5cf" + "reference": "be45764272e8873c72dbe3d2edcfdfcc3bc9f727" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/3cf1b6d4f0c820a2cf8bcaec39fc698f3443b5cf", - "reference": "3cf1b6d4f0c820a2cf8bcaec39fc698f3443b5cf", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/be45764272e8873c72dbe3d2edcfdfcc3bc9f727", + "reference": "be45764272e8873c72dbe3d2edcfdfcc3bc9f727", "shasum": "" }, "require": { "php": "^7.2.5 || ^8.0", "psr/http-factory": "^1.0", - "psr/http-message": "^1.0", + "psr/http-message": "^1.1 || ^2.0", "ralouphie/getallheaders": "^3.0" }, "provide": { @@ -1964,9 +2049,6 @@ "bamarni-bin": { "bin-links": true, "forward-command": false - }, - "branch-alias": { - "dev-master": "2.4-dev" } }, "autoload": { @@ -2028,7 +2110,7 @@ ], "support": { "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.4.4" + "source": "https://github.com/guzzle/psr7/tree/2.6.1" }, "funding": [ { @@ -2044,20 +2126,20 @@ "type": "tidelift" } ], - "time": "2023-03-09T13:19:02+00:00" + "time": "2023-08-27T10:13:57+00:00" }, { "name": "guzzlehttp/uri-template", - "version": "v1.0.1", + "version": "v1.0.2", "source": { "type": "git", "url": "https://github.com/guzzle/uri-template.git", - "reference": "b945d74a55a25a949158444f09ec0d3c120d69e2" + "reference": "61bf437fc2197f587f6857d3ff903a24f1731b5d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/uri-template/zipball/b945d74a55a25a949158444f09ec0d3c120d69e2", - "reference": "b945d74a55a25a949158444f09ec0d3c120d69e2", + "url": "https://api.github.com/repos/guzzle/uri-template/zipball/61bf437fc2197f587f6857d3ff903a24f1731b5d", + "reference": "61bf437fc2197f587f6857d3ff903a24f1731b5d", "shasum": "" }, "require": { @@ -2065,15 +2147,11 @@ "symfony/polyfill-php80": "^1.17" }, "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.1", "phpunit/phpunit": "^8.5.19 || ^9.5.8", "uri-template/tests": "1.0.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, "autoload": { "psr-4": { "GuzzleHttp\\UriTemplate\\": "src" @@ -2112,7 +2190,7 @@ ], "support": { "issues": "https://github.com/guzzle/uri-template/issues", - "source": "https://github.com/guzzle/uri-template/tree/v1.0.1" + "source": "https://github.com/guzzle/uri-template/tree/v1.0.2" }, "funding": [ { @@ -2128,7 +2206,7 @@ "type": "tidelift" } ], - "time": "2021-10-07T12:57:01+00:00" + "time": "2023-08-27T10:19:19+00:00" }, { "name": "http-interop/http-factory-guzzle", @@ -2249,16 +2327,16 @@ }, { "name": "laravel/framework", - "version": "v10.5.1", + "version": "v10.23.0", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "485f22333e8c1dff5bae0fe0421c1e2e139713de" + "reference": "7d6a79ce8ff52a4d0d385ac290dcc048aa514ce7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/485f22333e8c1dff5bae0fe0421c1e2e139713de", - "reference": "485f22333e8c1dff5bae0fe0421c1e2e139713de", + "url": "https://api.github.com/repos/laravel/framework/zipball/7d6a79ce8ff52a4d0d385ac290dcc048aa514ce7", + "reference": "7d6a79ce8ff52a4d0d385ac290dcc048aa514ce7", "shasum": "" }, "require": { @@ -2276,11 +2354,12 @@ "ext-tokenizer": "*", "fruitcake/php-cors": "^1.2", "guzzlehttp/uri-template": "^1.0", + "laravel/prompts": "^0.1", "laravel/serializable-closure": "^1.3", "league/commonmark": "^2.2.1", "league/flysystem": "^3.8.0", "monolog/monolog": "^3.0", - "nesbot/carbon": "^2.62.1", + "nesbot/carbon": "^2.67", "nunomaduro/termwind": "^1.13", "php": "^8.1", "psr/container": "^1.1.1|^2.0.1", @@ -2357,9 +2436,8 @@ "league/flysystem-read-only": "^3.3", "league/flysystem-sftp-v3": "^3.0", "mockery/mockery": "^1.5.1", - "orchestra/testbench-core": "^8.1", + "orchestra/testbench-core": "^8.4", "pda/pheanstalk": "^4.0", - "phpstan/phpdoc-parser": "^1.15", "phpstan/phpstan": "^1.4.7", "phpunit/phpunit": "^10.0.7", "predis/predis": "^2.0.2", @@ -2445,20 +2523,68 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2023-03-29T15:09:16+00:00" + "time": "2023-09-12T15:55:31+00:00" + }, + { + "name": "laravel/prompts", + "version": "v0.1.6", + "source": { + "type": "git", + "url": "https://github.com/laravel/prompts.git", + "reference": "b514c5620e1b3b61221b0024dc88def26d9654f4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/prompts/zipball/b514c5620e1b3b61221b0024dc88def26d9654f4", + "reference": "b514c5620e1b3b61221b0024dc88def26d9654f4", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "illuminate/collections": "^10.0|^11.0", + "php": "^8.1", + "symfony/console": "^6.2" + }, + "require-dev": { + "mockery/mockery": "^1.5", + "pestphp/pest": "^2.3", + "phpstan/phpstan": "^1.10", + "phpstan/phpstan-mockery": "^1.1" + }, + "suggest": { + "ext-pcntl": "Required for the spinner to be animated." + }, + "type": "library", + "autoload": { + "files": [ + "src/helpers.php" + ], + "psr-4": { + "Laravel\\Prompts\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "support": { + "issues": "https://github.com/laravel/prompts/issues", + "source": "https://github.com/laravel/prompts/tree/v0.1.6" + }, + "time": "2023-08-18T13:32:23+00:00" }, { "name": "laravel/serializable-closure", - "version": "v1.3.0", + "version": "v1.3.1", "source": { "type": "git", "url": "https://github.com/laravel/serializable-closure.git", - "reference": "f23fe9d4e95255dacee1bf3525e0810d1a1b0f37" + "reference": "e5a3057a5591e1cfe8183034b0203921abe2c902" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/f23fe9d4e95255dacee1bf3525e0810d1a1b0f37", - "reference": "f23fe9d4e95255dacee1bf3525e0810d1a1b0f37", + "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/e5a3057a5591e1cfe8183034b0203921abe2c902", + "reference": "e5a3057a5591e1cfe8183034b0203921abe2c902", "shasum": "" }, "require": { @@ -2505,20 +2631,20 @@ "issues": "https://github.com/laravel/serializable-closure/issues", "source": "https://github.com/laravel/serializable-closure" }, - "time": "2023-01-30T18:31:20+00:00" + "time": "2023-07-14T13:56:28+00:00" }, { "name": "laravel/socialite", - "version": "v5.6.1", + "version": "v5.9.0", "source": { "type": "git", "url": "https://github.com/laravel/socialite.git", - "reference": "a14a177f2cc71d8add71e2b19e00800e83bdda09" + "reference": "14acfa3262875f180fba51efe3c7aaa089a9ef24" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/socialite/zipball/a14a177f2cc71d8add71e2b19e00800e83bdda09", - "reference": "a14a177f2cc71d8add71e2b19e00800e83bdda09", + "url": "https://api.github.com/repos/laravel/socialite/zipball/14acfa3262875f180fba51efe3c7aaa089a9ef24", + "reference": "14acfa3262875f180fba51efe3c7aaa089a9ef24", "shasum": "" }, "require": { @@ -2533,6 +2659,7 @@ "require-dev": { "mockery/mockery": "^1.0", "orchestra/testbench": "^4.0|^5.0|^6.0|^7.0|^8.0", + "phpstan/phpstan": "^1.10", "phpunit/phpunit": "^8.0|^9.3" }, "type": "library", @@ -2574,7 +2701,7 @@ "issues": "https://github.com/laravel/socialite/issues", "source": "https://github.com/laravel/socialite" }, - "time": "2023-01-20T15:42:35+00:00" + "time": "2023-09-05T15:20:21+00:00" }, { "name": "laravel/tinker", @@ -2646,16 +2773,16 @@ }, { "name": "league/commonmark", - "version": "2.4.0", + "version": "2.4.1", "source": { "type": "git", "url": "https://github.com/thephpleague/commonmark.git", - "reference": "d44a24690f16b8c1808bf13b1bd54ae4c63ea048" + "reference": "3669d6d5f7a47a93c08ddff335e6d945481a1dd5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/d44a24690f16b8c1808bf13b1bd54ae4c63ea048", - "reference": "d44a24690f16b8c1808bf13b1bd54ae4c63ea048", + "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/3669d6d5f7a47a93c08ddff335e6d945481a1dd5", + "reference": "3669d6d5f7a47a93c08ddff335e6d945481a1dd5", "shasum": "" }, "require": { @@ -2748,7 +2875,7 @@ "type": "tidelift" } ], - "time": "2023-03-24T15:16:10+00:00" + "time": "2023-08-30T16:55:00+00:00" }, { "name": "league/config", @@ -2834,23 +2961,26 @@ }, { "name": "league/flysystem", - "version": "3.12.3", + "version": "3.16.0", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem.git", - "reference": "81e87e74dd5213795c7846d65089712d2dda90ce" + "reference": "4fdf372ca6b63c6e281b1c01a624349ccb757729" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/81e87e74dd5213795c7846d65089712d2dda90ce", - "reference": "81e87e74dd5213795c7846d65089712d2dda90ce", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/4fdf372ca6b63c6e281b1c01a624349ccb757729", + "reference": "4fdf372ca6b63c6e281b1c01a624349ccb757729", "shasum": "" }, "require": { + "league/flysystem-local": "^3.0.0", "league/mime-type-detection": "^1.0.0", "php": "^8.0.2" }, "conflict": { + "async-aws/core": "<1.19.0", + "async-aws/s3": "<1.14.0", "aws/aws-sdk-php": "3.209.31 || 3.210.0", "guzzlehttp/guzzle": "<7.0", "guzzlehttp/ringphp": "<1.1.1", @@ -2870,7 +3000,7 @@ "microsoft/azure-storage-blob": "^1.1", "phpseclib/phpseclib": "^3.0.14", "phpstan/phpstan": "^0.12.26", - "phpunit/phpunit": "^9.5.11", + "phpunit/phpunit": "^9.5.11|^10.0", "sabre/dav": "^4.3.1" }, "type": "library", @@ -2905,7 +3035,7 @@ ], "support": { "issues": "https://github.com/thephpleague/flysystem/issues", - "source": "https://github.com/thephpleague/flysystem/tree/3.12.3" + "source": "https://github.com/thephpleague/flysystem/tree/3.16.0" }, "funding": [ { @@ -2915,36 +3045,92 @@ { "url": "https://github.com/frankdejonge", "type": "github" + } + ], + "time": "2023-09-07T19:22:17+00:00" + }, + { + "name": "league/flysystem-local", + "version": "3.16.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/flysystem-local.git", + "reference": "ec7383f25642e6fd4bb0c9554fc2311245391781" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/flysystem-local/zipball/ec7383f25642e6fd4bb0c9554fc2311245391781", + "reference": "ec7383f25642e6fd4bb0c9554fc2311245391781", + "shasum": "" + }, + "require": { + "ext-fileinfo": "*", + "league/flysystem": "^3.0.0", + "league/mime-type-detection": "^1.0.0", + "php": "^8.0.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\Flysystem\\Local\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frankdejonge.nl" + } + ], + "description": "Local filesystem adapter for Flysystem.", + "keywords": [ + "Flysystem", + "file", + "files", + "filesystem", + "local" + ], + "support": { + "issues": "https://github.com/thephpleague/flysystem-local/issues", + "source": "https://github.com/thephpleague/flysystem-local/tree/3.16.0" + }, + "funding": [ + { + "url": "https://ecologi.com/frankdejonge", + "type": "custom" }, { - "url": "https://tidelift.com/funding/github/packagist/league/flysystem", - "type": "tidelift" + "url": "https://github.com/frankdejonge", + "type": "github" } ], - "time": "2023-02-18T15:32:41+00:00" + "time": "2023-08-30T10:23:59+00:00" }, { "name": "league/mime-type-detection", - "version": "1.11.0", + "version": "1.13.0", "source": { "type": "git", "url": "https://github.com/thephpleague/mime-type-detection.git", - "reference": "ff6248ea87a9f116e78edd6002e39e5128a0d4dd" + "reference": "a6dfb1194a2946fcdc1f38219445234f65b35c96" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/ff6248ea87a9f116e78edd6002e39e5128a0d4dd", - "reference": "ff6248ea87a9f116e78edd6002e39e5128a0d4dd", + "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/a6dfb1194a2946fcdc1f38219445234f65b35c96", + "reference": "a6dfb1194a2946fcdc1f38219445234f65b35c96", "shasum": "" }, "require": { "ext-fileinfo": "*", - "php": "^7.2 || ^8.0" + "php": "^7.4 || ^8.0" }, "require-dev": { "friendsofphp/php-cs-fixer": "^3.2", "phpstan/phpstan": "^0.12.68", - "phpunit/phpunit": "^8.5.8 || ^9.3" + "phpunit/phpunit": "^8.5.8 || ^9.3 || ^10.0" }, "type": "library", "autoload": { @@ -2965,7 +3151,7 @@ "description": "Mime-type detection for Flysystem", "support": { "issues": "https://github.com/thephpleague/mime-type-detection/issues", - "source": "https://github.com/thephpleague/mime-type-detection/tree/1.11.0" + "source": "https://github.com/thephpleague/mime-type-detection/tree/1.13.0" }, "funding": [ { @@ -2977,7 +3163,7 @@ "type": "tidelift" } ], - "time": "2022-04-17T13:12:02+00:00" + "time": "2023-08-05T12:09:49+00:00" }, { "name": "league/oauth1-client", @@ -3607,16 +3793,16 @@ }, { "name": "monolog/monolog", - "version": "3.3.1", + "version": "3.4.0", "source": { "type": "git", "url": "https://github.com/Seldaek/monolog.git", - "reference": "9b5daeaffce5b926cac47923798bba91059e60e2" + "reference": "e2392369686d420ca32df3803de28b5d6f76867d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/9b5daeaffce5b926cac47923798bba91059e60e2", - "reference": "9b5daeaffce5b926cac47923798bba91059e60e2", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/e2392369686d420ca32df3803de28b5d6f76867d", + "reference": "e2392369686d420ca32df3803de28b5d6f76867d", "shasum": "" }, "require": { @@ -3631,7 +3817,7 @@ "doctrine/couchdb": "~1.0@dev", "elasticsearch/elasticsearch": "^7 || ^8", "ext-json": "*", - "graylog2/gelf-php": "^1.4.2 || ^2@dev", + "graylog2/gelf-php": "^1.4.2 || ^2.0", "guzzlehttp/guzzle": "^7.4.5", "guzzlehttp/psr7": "^2.2", "mongodb/mongodb": "^1.8", @@ -3639,7 +3825,7 @@ "phpstan/phpstan": "^1.9", "phpstan/phpstan-deprecation-rules": "^1.0", "phpstan/phpstan-strict-rules": "^1.4", - "phpunit/phpunit": "^9.5.26", + "phpunit/phpunit": "^10.1", "predis/predis": "^1.1 || ^2", "ruflin/elastica": "^7", "symfony/mailer": "^5.4 || ^6", @@ -3692,7 +3878,7 @@ ], "support": { "issues": "https://github.com/Seldaek/monolog/issues", - "source": "https://github.com/Seldaek/monolog/tree/3.3.1" + "source": "https://github.com/Seldaek/monolog/tree/3.4.0" }, "funding": [ { @@ -3704,7 +3890,7 @@ "type": "tidelift" } ], - "time": "2023-02-06T13:46:10+00:00" + "time": "2023-06-21T08:46:11+00:00" }, { "name": "myclabs/php-enum", @@ -3771,25 +3957,29 @@ }, { "name": "nesbot/carbon", - "version": "2.66.0", + "version": "2.70.0", "source": { "type": "git", "url": "https://github.com/briannesbitt/Carbon.git", - "reference": "496712849902241f04902033b0441b269effe001" + "reference": "d3298b38ea8612e5f77d38d1a99438e42f70341d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/496712849902241f04902033b0441b269effe001", - "reference": "496712849902241f04902033b0441b269effe001", + "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/d3298b38ea8612e5f77d38d1a99438e42f70341d", + "reference": "d3298b38ea8612e5f77d38d1a99438e42f70341d", "shasum": "" }, "require": { "ext-json": "*", "php": "^7.1.8 || ^8.0", + "psr/clock": "^1.0", "symfony/polyfill-mbstring": "^1.0", "symfony/polyfill-php80": "^1.16", "symfony/translation": "^3.4 || ^4.0 || ^5.0 || ^6.0" }, + "provide": { + "psr/clock-implementation": "1.0" + }, "require-dev": { "doctrine/dbal": "^2.0 || ^3.1.4", "doctrine/orm": "^2.7", @@ -3869,25 +4059,25 @@ "type": "tidelift" } ], - "time": "2023-01-29T18:53:47+00:00" + "time": "2023-09-07T16:43:50+00:00" }, { "name": "nette/schema", - "version": "v1.2.3", + "version": "v1.2.4", "source": { "type": "git", "url": "https://github.com/nette/schema.git", - "reference": "abbdbb70e0245d5f3bf77874cea1dfb0c930d06f" + "reference": "c9ff517a53903b3d4e29ec547fb20feecb05b8ab" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nette/schema/zipball/abbdbb70e0245d5f3bf77874cea1dfb0c930d06f", - "reference": "abbdbb70e0245d5f3bf77874cea1dfb0c930d06f", + "url": "https://api.github.com/repos/nette/schema/zipball/c9ff517a53903b3d4e29ec547fb20feecb05b8ab", + "reference": "c9ff517a53903b3d4e29ec547fb20feecb05b8ab", "shasum": "" }, "require": { "nette/utils": "^2.5.7 || ^3.1.5 || ^4.0", - "php": ">=7.1 <8.3" + "php": "7.1 - 8.3" }, "require-dev": { "nette/tester": "^2.3 || ^2.4", @@ -3929,26 +4119,26 @@ ], "support": { "issues": "https://github.com/nette/schema/issues", - "source": "https://github.com/nette/schema/tree/v1.2.3" + "source": "https://github.com/nette/schema/tree/v1.2.4" }, - "time": "2022-10-13T01:24:26+00:00" + "time": "2023-08-05T18:56:25+00:00" }, { "name": "nette/utils", - "version": "v4.0.0", + "version": "v4.0.1", "source": { "type": "git", "url": "https://github.com/nette/utils.git", - "reference": "cacdbf5a91a657ede665c541eda28941d4b09c1e" + "reference": "9124157137da01b1f5a5a22d6486cb975f26db7e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nette/utils/zipball/cacdbf5a91a657ede665c541eda28941d4b09c1e", - "reference": "cacdbf5a91a657ede665c541eda28941d4b09c1e", + "url": "https://api.github.com/repos/nette/utils/zipball/9124157137da01b1f5a5a22d6486cb975f26db7e", + "reference": "9124157137da01b1f5a5a22d6486cb975f26db7e", "shasum": "" }, "require": { - "php": ">=8.0 <8.3" + "php": ">=8.0 <8.4" }, "conflict": { "nette/finder": "<3", @@ -3956,7 +4146,7 @@ }, "require-dev": { "jetbrains/phpstorm-attributes": "dev-master", - "nette/tester": "^2.4", + "nette/tester": "^2.5", "phpstan/phpstan": "^1.0", "tracy/tracy": "^2.9" }, @@ -4016,9 +4206,9 @@ ], "support": { "issues": "https://github.com/nette/utils/issues", - "source": "https://github.com/nette/utils/tree/v4.0.0" + "source": "https://github.com/nette/utils/tree/v4.0.1" }, - "time": "2023-02-02T10:41:53+00:00" + "time": "2023-07-30T15:42:21+00:00" }, { "name": "nikic/php-parser", @@ -4164,38 +4354,39 @@ }, { "name": "nyholm/psr7", - "version": "1.5.1", + "version": "1.8.0", "source": { "type": "git", "url": "https://github.com/Nyholm/psr7.git", - "reference": "f734364e38a876a23be4d906a2a089e1315be18a" + "reference": "3cb4d163b58589e47b35103e8e5e6a6a475b47be" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Nyholm/psr7/zipball/f734364e38a876a23be4d906a2a089e1315be18a", - "reference": "f734364e38a876a23be4d906a2a089e1315be18a", + "url": "https://api.github.com/repos/Nyholm/psr7/zipball/3cb4d163b58589e47b35103e8e5e6a6a475b47be", + "reference": "3cb4d163b58589e47b35103e8e5e6a6a475b47be", "shasum": "" }, "require": { - "php": ">=7.1", - "php-http/message-factory": "^1.0", + "php": ">=7.2", "psr/http-factory": "^1.0", - "psr/http-message": "^1.0" + "psr/http-message": "^1.1 || ^2.0" }, "provide": { + "php-http/message-factory-implementation": "1.0", "psr/http-factory-implementation": "1.0", "psr/http-message-implementation": "1.0" }, "require-dev": { "http-interop/http-factory-tests": "^0.9", + "php-http/message-factory": "^1.0", "php-http/psr7-integration-tests": "^1.0", - "phpunit/phpunit": "^7.5 || 8.5 || 9.4", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.4", "symfony/error-handler": "^4.4" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.4-dev" + "dev-master": "1.8-dev" } }, "autoload": { @@ -4225,7 +4416,7 @@ ], "support": { "issues": "https://github.com/Nyholm/psr7/issues", - "source": "https://github.com/Nyholm/psr7/tree/1.5.1" + "source": "https://github.com/Nyholm/psr7/tree/1.8.0" }, "funding": [ { @@ -4237,30 +4428,29 @@ "type": "github" } ], - "time": "2022-06-22T07:13:36+00:00" + "time": "2023-05-02T11:26:24+00:00" }, { "name": "php-http/client-common", - "version": "2.6.0", + "version": "2.7.0", "source": { "type": "git", "url": "https://github.com/php-http/client-common.git", - "reference": "45db684cd4e186dcdc2b9c06b22970fe123796c0" + "reference": "880509727a447474d2a71b7d7fa5d268ddd3db4b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-http/client-common/zipball/45db684cd4e186dcdc2b9c06b22970fe123796c0", - "reference": "45db684cd4e186dcdc2b9c06b22970fe123796c0", + "url": "https://api.github.com/repos/php-http/client-common/zipball/880509727a447474d2a71b7d7fa5d268ddd3db4b", + "reference": "880509727a447474d2a71b7d7fa5d268ddd3db4b", "shasum": "" }, "require": { "php": "^7.1 || ^8.0", "php-http/httplug": "^2.0", "php-http/message": "^1.6", - "php-http/message-factory": "^1.0", "psr/http-client": "^1.0", "psr/http-factory": "^1.0", - "psr/http-message": "^1.0", + "psr/http-message": "^1.0 || ^2.0", "symfony/options-resolver": "~4.0.15 || ~4.1.9 || ^4.2.1 || ^5.0 || ^6.0", "symfony/polyfill-php80": "^1.17" }, @@ -4270,7 +4460,7 @@ "nyholm/psr7": "^1.2", "phpspec/phpspec": "^5.1 || ^6.3 || ^7.1", "phpspec/prophecy": "^1.10.2", - "phpunit/phpunit": "^7.5.15 || ^8.5 || ^9.3" + "phpunit/phpunit": "^7.5.20 || ^8.5.33 || ^9.6.7" }, "suggest": { "ext-json": "To detect JSON responses with the ContentTypePlugin", @@ -4280,11 +4470,6 @@ "php-http/stopwatch-plugin": "Symfony Stopwatch plugin" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.3.x-dev" - } - }, "autoload": { "psr-4": { "Http\\Client\\Common\\": "src/" @@ -4310,22 +4495,22 @@ ], "support": { "issues": "https://github.com/php-http/client-common/issues", - "source": "https://github.com/php-http/client-common/tree/2.6.0" + "source": "https://github.com/php-http/client-common/tree/2.7.0" }, - "time": "2022-09-29T09:59:43+00:00" + "time": "2023-05-17T06:46:59+00:00" }, { "name": "php-http/discovery", - "version": "1.15.3", + "version": "1.19.1", "source": { "type": "git", "url": "https://github.com/php-http/discovery.git", - "reference": "3ccd28dd9fb34b52db946abea1b538568e34eae8" + "reference": "57f3de01d32085fea20865f9b16fb0e69347c39e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-http/discovery/zipball/3ccd28dd9fb34b52db946abea1b538568e34eae8", - "reference": "3ccd28dd9fb34b52db946abea1b538568e34eae8", + "url": "https://api.github.com/repos/php-http/discovery/zipball/57f3de01d32085fea20865f9b16fb0e69347c39e", + "reference": "57f3de01d32085fea20865f9b16fb0e69347c39e", "shasum": "" }, "require": { @@ -4333,7 +4518,8 @@ "php": "^7.1 || ^8.0" }, "conflict": { - "nyholm/psr7": "<1.0" + "nyholm/psr7": "<1.0", + "zendframework/zend-diactoros": "*" }, "provide": { "php-http/async-client-implementation": "*", @@ -4358,7 +4544,10 @@ "autoload": { "psr-4": { "Http\\Discovery\\": "src/" - } + }, + "exclude-from-classmap": [ + "src/Composer/Plugin.php" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -4384,40 +4573,35 @@ ], "support": { "issues": "https://github.com/php-http/discovery/issues", - "source": "https://github.com/php-http/discovery/tree/1.15.3" + "source": "https://github.com/php-http/discovery/tree/1.19.1" }, - "time": "2023-03-31T14:40:37+00:00" + "time": "2023-07-11T07:02:26+00:00" }, { "name": "php-http/httplug", - "version": "2.3.0", + "version": "2.4.0", "source": { "type": "git", "url": "https://github.com/php-http/httplug.git", - "reference": "f640739f80dfa1152533976e3c112477f69274eb" + "reference": "625ad742c360c8ac580fcc647a1541d29e257f67" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-http/httplug/zipball/f640739f80dfa1152533976e3c112477f69274eb", - "reference": "f640739f80dfa1152533976e3c112477f69274eb", + "url": "https://api.github.com/repos/php-http/httplug/zipball/625ad742c360c8ac580fcc647a1541d29e257f67", + "reference": "625ad742c360c8ac580fcc647a1541d29e257f67", "shasum": "" }, "require": { "php": "^7.1 || ^8.0", "php-http/promise": "^1.1", "psr/http-client": "^1.0", - "psr/http-message": "^1.0" + "psr/http-message": "^1.0 || ^2.0" }, "require-dev": { - "friends-of-phpspec/phpspec-code-coverage": "^4.1", - "phpspec/phpspec": "^5.1 || ^6.0" + "friends-of-phpspec/phpspec-code-coverage": "^4.1 || ^5.0 || ^6.0", + "phpspec/phpspec": "^5.1 || ^6.0 || ^7.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.x-dev" - } - }, "autoload": { "psr-4": { "Http\\Client\\": "src/" @@ -4446,29 +4630,28 @@ ], "support": { "issues": "https://github.com/php-http/httplug/issues", - "source": "https://github.com/php-http/httplug/tree/2.3.0" + "source": "https://github.com/php-http/httplug/tree/2.4.0" }, - "time": "2022-02-21T09:52:22+00:00" + "time": "2023-04-14T15:10:03+00:00" }, { "name": "php-http/message", - "version": "1.13.0", + "version": "1.16.0", "source": { "type": "git", "url": "https://github.com/php-http/message.git", - "reference": "7886e647a30a966a1a8d1dad1845b71ca8678361" + "reference": "47a14338bf4ebd67d317bf1144253d7db4ab55fd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-http/message/zipball/7886e647a30a966a1a8d1dad1845b71ca8678361", - "reference": "7886e647a30a966a1a8d1dad1845b71ca8678361", + "url": "https://api.github.com/repos/php-http/message/zipball/47a14338bf4ebd67d317bf1144253d7db4ab55fd", + "reference": "47a14338bf4ebd67d317bf1144253d7db4ab55fd", "shasum": "" }, "require": { "clue/stream-filter": "^1.5", - "php": "^7.1 || ^8.0", - "php-http/message-factory": "^1.0.2", - "psr/http-message": "^1.0" + "php": "^7.2 || ^8.0", + "psr/http-message": "^1.1 || ^2.0" }, "provide": { "php-http/message-factory-implementation": "1.0" @@ -4476,8 +4659,9 @@ "require-dev": { "ergebnis/composer-normalize": "^2.6", "ext-zlib": "*", - "guzzlehttp/psr7": "^1.0", - "laminas/laminas-diactoros": "^2.0", + "guzzlehttp/psr7": "^1.0 || ^2.0", + "laminas/laminas-diactoros": "^2.0 || ^3.0", + "php-http/message-factory": "^1.0.2", "phpspec/phpspec": "^5.1 || ^6.3 || ^7.1", "slim/slim": "^3.0" }, @@ -4488,11 +4672,6 @@ "slim/slim": "Used with Slim Framework PSR-7 implementation" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.10-dev" - } - }, "autoload": { "files": [ "src/filters.php" @@ -4520,32 +4699,32 @@ ], "support": { "issues": "https://github.com/php-http/message/issues", - "source": "https://github.com/php-http/message/tree/1.13.0" + "source": "https://github.com/php-http/message/tree/1.16.0" }, - "time": "2022-02-11T13:41:14+00:00" + "time": "2023-05-17T06:43:38+00:00" }, { "name": "php-http/message-factory", - "version": "v1.0.2", + "version": "1.1.0", "source": { "type": "git", "url": "https://github.com/php-http/message-factory.git", - "reference": "a478cb11f66a6ac48d8954216cfed9aa06a501a1" + "reference": "4d8778e1c7d405cbb471574821c1ff5b68cc8f57" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-http/message-factory/zipball/a478cb11f66a6ac48d8954216cfed9aa06a501a1", - "reference": "a478cb11f66a6ac48d8954216cfed9aa06a501a1", + "url": "https://api.github.com/repos/php-http/message-factory/zipball/4d8778e1c7d405cbb471574821c1ff5b68cc8f57", + "reference": "4d8778e1c7d405cbb471574821c1ff5b68cc8f57", "shasum": "" }, "require": { "php": ">=5.4", - "psr/http-message": "^1.0" + "psr/http-message": "^1.0 || ^2.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-master": "1.x-dev" } }, "autoload": { @@ -4574,9 +4753,10 @@ ], "support": { "issues": "https://github.com/php-http/message-factory/issues", - "source": "https://github.com/php-http/message-factory/tree/master" + "source": "https://github.com/php-http/message-factory/tree/1.1.0" }, - "time": "2015-12-19T14:08:53+00:00" + "abandoned": "psr/http-factory", + "time": "2023-04-14T14:16:17+00:00" }, { "name": "php-http/promise", @@ -4864,6 +5044,54 @@ }, "time": "2021-02-03T23:26:27+00:00" }, + { + "name": "psr/clock", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/clock.git", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/clock/zipball/e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Psr\\Clock\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for reading the clock.", + "homepage": "https://github.com/php-fig/clock", + "keywords": [ + "clock", + "now", + "psr", + "psr-20", + "time" + ], + "support": { + "issues": "https://github.com/php-fig/clock/issues", + "source": "https://github.com/php-fig/clock/tree/1.0.0" + }, + "time": "2022-11-25T14:36:26+00:00" + }, { "name": "psr/container", "version": "2.0.2", @@ -4969,21 +5197,21 @@ }, { "name": "psr/http-client", - "version": "1.0.1", + "version": "1.0.2", "source": { "type": "git", "url": "https://github.com/php-fig/http-client.git", - "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621" + "reference": "0955afe48220520692d2d09f7ab7e0f93ffd6a31" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-client/zipball/2dfb5f6c5eff0e91e20e913f8c5452ed95b86621", - "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/0955afe48220520692d2d09f7ab7e0f93ffd6a31", + "reference": "0955afe48220520692d2d09f7ab7e0f93ffd6a31", "shasum": "" }, "require": { "php": "^7.0 || ^8.0", - "psr/http-message": "^1.0" + "psr/http-message": "^1.0 || ^2.0" }, "type": "library", "extra": { @@ -5003,7 +5231,7 @@ "authors": [ { "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "homepage": "https://www.php-fig.org/" } ], "description": "Common interface for HTTP clients", @@ -5015,27 +5243,27 @@ "psr-18" ], "support": { - "source": "https://github.com/php-fig/http-client/tree/master" + "source": "https://github.com/php-fig/http-client/tree/1.0.2" }, - "time": "2020-06-29T06:28:15+00:00" + "time": "2023-04-10T20:12:12+00:00" }, { "name": "psr/http-factory", - "version": "1.0.1", + "version": "1.0.2", "source": { "type": "git", "url": "https://github.com/php-fig/http-factory.git", - "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be" + "reference": "e616d01114759c4c489f93b099585439f795fe35" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-factory/zipball/12ac7fcd07e5b077433f5f2bee95b3a771bf61be", - "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/e616d01114759c4c489f93b099585439f795fe35", + "reference": "e616d01114759c4c489f93b099585439f795fe35", "shasum": "" }, "require": { "php": ">=7.0.0", - "psr/http-message": "^1.0" + "psr/http-message": "^1.0 || ^2.0" }, "type": "library", "extra": { @@ -5055,7 +5283,7 @@ "authors": [ { "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "homepage": "https://www.php-fig.org/" } ], "description": "Common interfaces for PSR-7 HTTP message factories", @@ -5070,31 +5298,31 @@ "response" ], "support": { - "source": "https://github.com/php-fig/http-factory/tree/master" + "source": "https://github.com/php-fig/http-factory/tree/1.0.2" }, - "time": "2019-04-30T12:38:16+00:00" + "time": "2023-04-10T20:10:41+00:00" }, { "name": "psr/http-message", - "version": "1.0.1", + "version": "1.1", "source": { "type": "git", "url": "https://github.com/php-fig/http-message.git", - "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" + "reference": "cb6ce4845ce34a8ad9e68117c10ee90a29919eba" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", - "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/cb6ce4845ce34a8ad9e68117c10ee90a29919eba", + "reference": "cb6ce4845ce34a8ad9e68117c10ee90a29919eba", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": "^7.2 || ^8.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "1.1.x-dev" } }, "autoload": { @@ -5123,9 +5351,9 @@ "response" ], "support": { - "source": "https://github.com/php-fig/http-message/tree/master" + "source": "https://github.com/php-fig/http-message/tree/1.1" }, - "time": "2016-08-06T14:39:51+00:00" + "time": "2023-04-04T09:50:52+00:00" }, { "name": "psr/log", @@ -5439,16 +5667,16 @@ }, { "name": "ramsey/uuid", - "version": "4.x-dev", + "version": "4.7.4", "source": { "type": "git", "url": "https://github.com/ramsey/uuid.git", - "reference": "8e955307d32dc9b6992440ff81321d3cb09db75a" + "reference": "60a4c63ab724854332900504274f6150ff26d286" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ramsey/uuid/zipball/8e955307d32dc9b6992440ff81321d3cb09db75a", - "reference": "8e955307d32dc9b6992440ff81321d3cb09db75a", + "url": "https://api.github.com/repos/ramsey/uuid/zipball/60a4c63ab724854332900504274f6150ff26d286", + "reference": "60a4c63ab724854332900504274f6150ff26d286", "shasum": "" }, "require": { @@ -5489,7 +5717,6 @@ "paragonie/random-lib": "Provides RandomLib for use with the RandomLibAdapter", "ramsey/uuid-doctrine": "Allows the use of Ramsey\\Uuid\\Uuid as Doctrine field type." }, - "default-branch": true, "type": "library", "extra": { "captainhook": { @@ -5516,7 +5743,7 @@ ], "support": { "issues": "https://github.com/ramsey/uuid/issues", - "source": "https://github.com/ramsey/uuid/tree/4.x" + "source": "https://github.com/ramsey/uuid/tree/4.7.4" }, "funding": [ { @@ -5528,7 +5755,7 @@ "type": "tidelift" } ], - "time": "2023-03-27T22:05:11+00:00" + "time": "2023-04-15T23:01:58+00:00" }, { "name": "ryangjchandler/blade-capture-directive", @@ -5610,21 +5837,21 @@ }, { "name": "sentry/sdk", - "version": "3.3.0", + "version": "3.5.0", "source": { "type": "git", "url": "https://github.com/getsentry/sentry-php-sdk.git", - "reference": "d0678fc7274dbb03046ed05cb24eb92945bedf8e" + "reference": "cd91b752f07c4bab9fb3b173f81af68a78a78d6d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/getsentry/sentry-php-sdk/zipball/d0678fc7274dbb03046ed05cb24eb92945bedf8e", - "reference": "d0678fc7274dbb03046ed05cb24eb92945bedf8e", + "url": "https://api.github.com/repos/getsentry/sentry-php-sdk/zipball/cd91b752f07c4bab9fb3b173f81af68a78a78d6d", + "reference": "cd91b752f07c4bab9fb3b173f81af68a78a78d6d", "shasum": "" }, "require": { "http-interop/http-factory-guzzle": "^1.0", - "sentry/sentry": "^3.9", + "sentry/sentry": "^3.19", "symfony/http-client": "^4.3|^5.0|^6.0" }, "type": "metapackage", @@ -5651,7 +5878,7 @@ ], "support": { "issues": "https://github.com/getsentry/sentry-php-sdk/issues", - "source": "https://github.com/getsentry/sentry-php-sdk/tree/3.3.0" + "source": "https://github.com/getsentry/sentry-php-sdk/tree/3.5.0" }, "funding": [ { @@ -5663,26 +5890,26 @@ "type": "custom" } ], - "time": "2022-10-11T09:05:00+00:00" + "time": "2023-06-12T17:50:36+00:00" }, { "name": "sentry/sentry", - "version": "3.17.0", + "version": "3.21.0", "source": { "type": "git", "url": "https://github.com/getsentry/sentry-php.git", - "reference": "95d2e932383cf684f77acff0d2a5aef5ad2f1933" + "reference": "624aafc22b84b089ffa43b71fb01e0096505ec4f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/getsentry/sentry-php/zipball/95d2e932383cf684f77acff0d2a5aef5ad2f1933", - "reference": "95d2e932383cf684f77acff0d2a5aef5ad2f1933", + "url": "https://api.github.com/repos/getsentry/sentry-php/zipball/624aafc22b84b089ffa43b71fb01e0096505ec4f", + "reference": "624aafc22b84b089ffa43b71fb01e0096505ec4f", "shasum": "" }, "require": { "ext-json": "*", "ext-mbstring": "*", - "guzzlehttp/promises": "^1.4", + "guzzlehttp/promises": "^1.5.3|^2.0", "jean85/pretty-package-versions": "^1.5|^2.0.4", "php": "^7.2|^8.0", "php-http/async-client-implementation": "^1.0", @@ -5690,6 +5917,7 @@ "php-http/discovery": "^1.15", "php-http/httplug": "^1.1|^2.0", "php-http/message": "^1.5", + "php-http/message-factory": "^1.1", "psr/http-factory": "^1.0", "psr/http-factory-implementation": "^1.0", "psr/log": "^1.0|^2.0|^3.0", @@ -5719,11 +5947,6 @@ "monolog/monolog": "Allow sending log messages to Sentry by using the included Monolog handler." }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.13.x-dev" - } - }, "autoload": { "files": [ "src/functions.php" @@ -5755,7 +5978,7 @@ ], "support": { "issues": "https://github.com/getsentry/sentry-php/issues", - "source": "https://github.com/getsentry/sentry-php/tree/3.17.0" + "source": "https://github.com/getsentry/sentry-php/tree/3.21.0" }, "funding": [ { @@ -5767,38 +5990,37 @@ "type": "custom" } ], - "time": "2023-03-26T21:54:06+00:00" + "time": "2023-07-31T15:31:24+00:00" }, { "name": "sentry/sentry-laravel", - "version": "3.3.2", + "version": "3.8.0", "source": { "type": "git", "url": "https://github.com/getsentry/sentry-laravel.git", - "reference": "c502e8b9005990d03f5ec5cc852e98a27c26056d" + "reference": "c7e7611553f9f90af10ed98dde1a680220f02e4d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/getsentry/sentry-laravel/zipball/c502e8b9005990d03f5ec5cc852e98a27c26056d", - "reference": "c502e8b9005990d03f5ec5cc852e98a27c26056d", + "url": "https://api.github.com/repos/getsentry/sentry-laravel/zipball/c7e7611553f9f90af10ed98dde1a680220f02e4d", + "reference": "c7e7611553f9f90af10ed98dde1a680220f02e4d", "shasum": "" }, "require": { "illuminate/support": "^6.0 | ^7.0 | ^8.0 | ^9.0 | ^10.0", "nyholm/psr7": "^1.0", "php": "^7.2 | ^8.0", - "sentry/sdk": "^3.3", - "sentry/sentry": "^3.16", + "sentry/sdk": "^3.4", + "sentry/sentry": "^3.20.1", "symfony/psr-http-message-bridge": "^1.0 | ^2.0" }, - "conflict": { - "laravel/lumen-framework": "*" - }, "require-dev": { "friendsofphp/php-cs-fixer": "^3.11", + "laravel/folio": "^1.0", "laravel/framework": "^6.0 | ^7.0 | ^8.0 | ^9.0 | ^10.0", "mockery/mockery": "^1.3", "orchestra/testbench": "^4.7 | ^5.1 | ^6.0 | ^7.0 | ^8.0", + "phpstan/phpstan": "^1.10", "phpunit/phpunit": "^8.4 | ^9.3" }, "type": "library", @@ -5848,7 +6070,7 @@ ], "support": { "issues": "https://github.com/getsentry/sentry-laravel/issues", - "source": "https://github.com/getsentry/sentry-laravel/tree/3.3.2" + "source": "https://github.com/getsentry/sentry-laravel/tree/3.8.0" }, "funding": [ { @@ -5860,7 +6082,7 @@ "type": "custom" } ], - "time": "2023-03-22T10:51:03+00:00" + "time": "2023-09-05T11:02:34+00:00" }, { "name": "socialiteproviders/manager", @@ -6155,16 +6377,16 @@ }, { "name": "spatie/laravel-markdown", - "version": "2.2.6", + "version": "2.4.0", "source": { "type": "git", "url": "https://github.com/spatie/laravel-markdown.git", - "reference": "d3542637093fa661f1d5bd58eff509b3868bace8" + "reference": "38d11c666ccdbf111535677c1d927b349b64aca4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-markdown/zipball/d3542637093fa661f1d5bd58eff509b3868bace8", - "reference": "d3542637093fa661f1d5bd58eff509b3868bace8", + "url": "https://api.github.com/repos/spatie/laravel-markdown/zipball/38d11c666ccdbf111535677c1d927b349b64aca4", + "reference": "38d11c666ccdbf111535677c1d927b349b64aca4", "shasum": "" }, "require": { @@ -6219,7 +6441,7 @@ "spatie" ], "support": { - "source": "https://github.com/spatie/laravel-markdown/tree/2.2.6" + "source": "https://github.com/spatie/laravel-markdown/tree/2.4.0" }, "funding": [ { @@ -6227,20 +6449,20 @@ "type": "github" } ], - "time": "2023-01-25T15:20:47+00:00" + "time": "2023-08-17T06:59:15+00:00" }, { "name": "spatie/laravel-package-tools", - "version": "1.14.2", + "version": "1.16.1", "source": { "type": "git", "url": "https://github.com/spatie/laravel-package-tools.git", - "reference": "bab62023a4745a61170ad5424184533685e73c2d" + "reference": "cc7c991555a37f9fa6b814aa03af73f88026a83d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-package-tools/zipball/bab62023a4745a61170ad5424184533685e73c2d", - "reference": "bab62023a4745a61170ad5424184533685e73c2d", + "url": "https://api.github.com/repos/spatie/laravel-package-tools/zipball/cc7c991555a37f9fa6b814aa03af73f88026a83d", + "reference": "cc7c991555a37f9fa6b814aa03af73f88026a83d", "shasum": "" }, "require": { @@ -6279,7 +6501,7 @@ ], "support": { "issues": "https://github.com/spatie/laravel-package-tools/issues", - "source": "https://github.com/spatie/laravel-package-tools/tree/1.14.2" + "source": "https://github.com/spatie/laravel-package-tools/tree/1.16.1" }, "funding": [ { @@ -6287,7 +6509,7 @@ "type": "github" } ], - "time": "2023-03-14T16:41:21+00:00" + "time": "2023-08-23T09:04:39+00:00" }, { "name": "spatie/shiki-php", @@ -6354,23 +6576,23 @@ }, { "name": "symfony/console", - "version": "v6.2.8", + "version": "v6.3.4", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "3582d68a64a86ec25240aaa521ec8bc2342b369b" + "reference": "eca495f2ee845130855ddf1cf18460c38966c8b6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/3582d68a64a86ec25240aaa521ec8bc2342b369b", - "reference": "3582d68a64a86ec25240aaa521ec8bc2342b369b", + "url": "https://api.github.com/repos/symfony/console/zipball/eca495f2ee845130855ddf1cf18460c38966c8b6", + "reference": "eca495f2ee845130855ddf1cf18460c38966c8b6", "shasum": "" }, "require": { "php": ">=8.1", - "symfony/deprecation-contracts": "^2.1|^3", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-mbstring": "~1.0", - "symfony/service-contracts": "^1.1|^2|^3", + "symfony/service-contracts": "^2.5|^3", "symfony/string": "^5.4|^6.0" }, "conflict": { @@ -6392,12 +6614,6 @@ "symfony/process": "^5.4|^6.0", "symfony/var-dumper": "^5.4|^6.0" }, - "suggest": { - "psr/log": "For using the console logger", - "symfony/event-dispatcher": "", - "symfony/lock": "", - "symfony/process": "" - }, "type": "library", "autoload": { "psr-4": { @@ -6430,7 +6646,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v6.2.8" + "source": "https://github.com/symfony/console/tree/v6.3.4" }, "funding": [ { @@ -6446,20 +6662,20 @@ "type": "tidelift" } ], - "time": "2023-03-29T21:42:15+00:00" + "time": "2023-08-16T10:10:12+00:00" }, { "name": "symfony/css-selector", - "version": "v6.2.7", + "version": "v6.3.2", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "aedf3cb0f5b929ec255d96bbb4909e9932c769e0" + "reference": "883d961421ab1709877c10ac99451632a3d6fa57" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/aedf3cb0f5b929ec255d96bbb4909e9932c769e0", - "reference": "aedf3cb0f5b929ec255d96bbb4909e9932c769e0", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/883d961421ab1709877c10ac99451632a3d6fa57", + "reference": "883d961421ab1709877c10ac99451632a3d6fa57", "shasum": "" }, "require": { @@ -6495,7 +6711,7 @@ "description": "Converts CSS selectors to XPath expressions", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/css-selector/tree/v6.2.7" + "source": "https://github.com/symfony/css-selector/tree/v6.3.2" }, "funding": [ { @@ -6511,20 +6727,20 @@ "type": "tidelift" } ], - "time": "2023-02-14T08:44:56+00:00" + "time": "2023-07-12T16:00:22+00:00" }, { "name": "symfony/deprecation-contracts", - "version": "v3.2.1", + "version": "v3.3.0", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e" + "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e", - "reference": "e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/7c3aff79d10325257a001fcf92d991f24fc967cf", + "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf", "shasum": "" }, "require": { @@ -6533,7 +6749,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "3.3-dev" + "dev-main": "3.4-dev" }, "thanks": { "name": "symfony/contracts", @@ -6562,7 +6778,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.2.1" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.3.0" }, "funding": [ { @@ -6578,20 +6794,20 @@ "type": "tidelift" } ], - "time": "2023-03-01T10:25:55+00:00" + "time": "2023-05-23T14:45:45+00:00" }, { "name": "symfony/error-handler", - "version": "v6.2.7", + "version": "v6.3.2", "source": { "type": "git", "url": "https://github.com/symfony/error-handler.git", - "reference": "61e90f94eb014054000bc902257d2763fac09166" + "reference": "85fd65ed295c4078367c784e8a5a6cee30348b7a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/61e90f94eb014054000bc902257d2763fac09166", - "reference": "61e90f94eb014054000bc902257d2763fac09166", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/85fd65ed295c4078367c784e8a5a6cee30348b7a", + "reference": "85fd65ed295c4078367c784e8a5a6cee30348b7a", "shasum": "" }, "require": { @@ -6599,8 +6815,11 @@ "psr/log": "^1|^2|^3", "symfony/var-dumper": "^5.4|^6.0" }, + "conflict": { + "symfony/deprecation-contracts": "<2.5" + }, "require-dev": { - "symfony/deprecation-contracts": "^2.1|^3", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/http-kernel": "^5.4|^6.0", "symfony/serializer": "^5.4|^6.0" }, @@ -6633,7 +6852,7 @@ "description": "Provides tools to manage errors and ease debugging PHP code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/error-handler/tree/v6.2.7" + "source": "https://github.com/symfony/error-handler/tree/v6.3.2" }, "funding": [ { @@ -6649,28 +6868,29 @@ "type": "tidelift" } ], - "time": "2023-02-14T08:44:56+00:00" + "time": "2023-07-16T17:05:46+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v6.2.8", + "version": "v6.3.2", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "04046f35fd7d72f9646e721fc2ecb8f9c67d3339" + "reference": "adb01fe097a4ee930db9258a3cc906b5beb5cf2e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/04046f35fd7d72f9646e721fc2ecb8f9c67d3339", - "reference": "04046f35fd7d72f9646e721fc2ecb8f9c67d3339", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/adb01fe097a4ee930db9258a3cc906b5beb5cf2e", + "reference": "adb01fe097a4ee930db9258a3cc906b5beb5cf2e", "shasum": "" }, "require": { "php": ">=8.1", - "symfony/event-dispatcher-contracts": "^2|^3" + "symfony/event-dispatcher-contracts": "^2.5|^3" }, "conflict": { - "symfony/dependency-injection": "<5.4" + "symfony/dependency-injection": "<5.4", + "symfony/service-contracts": "<2.5" }, "provide": { "psr/event-dispatcher-implementation": "1.0", @@ -6683,13 +6903,9 @@ "symfony/error-handler": "^5.4|^6.0", "symfony/expression-language": "^5.4|^6.0", "symfony/http-foundation": "^5.4|^6.0", - "symfony/service-contracts": "^1.1|^2|^3", + "symfony/service-contracts": "^2.5|^3", "symfony/stopwatch": "^5.4|^6.0" }, - "suggest": { - "symfony/dependency-injection": "", - "symfony/http-kernel": "" - }, "type": "library", "autoload": { "psr-4": { @@ -6716,7 +6932,7 @@ "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v6.2.8" + "source": "https://github.com/symfony/event-dispatcher/tree/v6.3.2" }, "funding": [ { @@ -6732,33 +6948,30 @@ "type": "tidelift" } ], - "time": "2023-03-20T16:06:02+00:00" + "time": "2023-07-06T06:56:43+00:00" }, { "name": "symfony/event-dispatcher-contracts", - "version": "v3.2.1", + "version": "v3.3.0", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher-contracts.git", - "reference": "0ad3b6f1e4e2da5690fefe075cd53a238646d8dd" + "reference": "a76aed96a42d2b521153fb382d418e30d18b59df" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/0ad3b6f1e4e2da5690fefe075cd53a238646d8dd", - "reference": "0ad3b6f1e4e2da5690fefe075cd53a238646d8dd", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/a76aed96a42d2b521153fb382d418e30d18b59df", + "reference": "a76aed96a42d2b521153fb382d418e30d18b59df", "shasum": "" }, "require": { "php": ">=8.1", "psr/event-dispatcher": "^1" }, - "suggest": { - "symfony/event-dispatcher-implementation": "" - }, "type": "library", "extra": { "branch-alias": { - "dev-main": "3.3-dev" + "dev-main": "3.4-dev" }, "thanks": { "name": "symfony/contracts", @@ -6795,7 +7008,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.2.1" + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.3.0" }, "funding": [ { @@ -6811,20 +7024,20 @@ "type": "tidelift" } ], - "time": "2023-03-01T10:32:47+00:00" + "time": "2023-05-23T14:45:45+00:00" }, { "name": "symfony/finder", - "version": "v6.2.7", + "version": "v6.3.3", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "20808dc6631aecafbe67c186af5dcb370be3a0eb" + "reference": "9915db259f67d21eefee768c1abcf1cc61b1fc9e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/20808dc6631aecafbe67c186af5dcb370be3a0eb", - "reference": "20808dc6631aecafbe67c186af5dcb370be3a0eb", + "url": "https://api.github.com/repos/symfony/finder/zipball/9915db259f67d21eefee768c1abcf1cc61b1fc9e", + "reference": "9915db259f67d21eefee768c1abcf1cc61b1fc9e", "shasum": "" }, "require": { @@ -6859,7 +7072,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v6.2.7" + "source": "https://github.com/symfony/finder/tree/v6.3.3" }, "funding": [ { @@ -6875,28 +7088,32 @@ "type": "tidelift" } ], - "time": "2023-02-16T09:57:23+00:00" + "time": "2023-07-31T08:31:44+00:00" }, { "name": "symfony/http-client", - "version": "v6.2.8", + "version": "v6.3.2", "source": { "type": "git", "url": "https://github.com/symfony/http-client.git", - "reference": "66391ba3a8862c560e1d9134c96d9bd2a619b477" + "reference": "15f9f4bad62bfcbe48b5dedd866f04a08fc7ff00" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client/zipball/66391ba3a8862c560e1d9134c96d9bd2a619b477", - "reference": "66391ba3a8862c560e1d9134c96d9bd2a619b477", + "url": "https://api.github.com/repos/symfony/http-client/zipball/15f9f4bad62bfcbe48b5dedd866f04a08fc7ff00", + "reference": "15f9f4bad62bfcbe48b5dedd866f04a08fc7ff00", "shasum": "" }, "require": { "php": ">=8.1", "psr/log": "^1|^2|^3", - "symfony/deprecation-contracts": "^2.1|^3", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/http-client-contracts": "^3", - "symfony/service-contracts": "^1.0|^2|^3" + "symfony/service-contracts": "^2.5|^3" + }, + "conflict": { + "php-http/discovery": "<1.15", + "symfony/http-foundation": "<6.3" }, "provide": { "php-http/async-client-implementation": "*", @@ -6947,7 +7164,7 @@ "http" ], "support": { - "source": "https://github.com/symfony/http-client/tree/v6.2.8" + "source": "https://github.com/symfony/http-client/tree/v6.3.2" }, "funding": [ { @@ -6963,32 +7180,29 @@ "type": "tidelift" } ], - "time": "2023-03-31T09:14:44+00:00" + "time": "2023-07-05T08:41:27+00:00" }, { "name": "symfony/http-client-contracts", - "version": "v3.2.1", + "version": "v3.3.0", "source": { "type": "git", "url": "https://github.com/symfony/http-client-contracts.git", - "reference": "df2ecd6cb70e73c1080e6478aea85f5f4da2c48b" + "reference": "3b66325d0176b4ec826bffab57c9037d759c31fb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/df2ecd6cb70e73c1080e6478aea85f5f4da2c48b", - "reference": "df2ecd6cb70e73c1080e6478aea85f5f4da2c48b", + "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/3b66325d0176b4ec826bffab57c9037d759c31fb", + "reference": "3b66325d0176b4ec826bffab57c9037d759c31fb", "shasum": "" }, "require": { "php": ">=8.1" }, - "suggest": { - "symfony/http-client-implementation": "" - }, "type": "library", "extra": { "branch-alias": { - "dev-main": "3.3-dev" + "dev-main": "3.4-dev" }, "thanks": { "name": "symfony/contracts", @@ -7028,7 +7242,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/http-client-contracts/tree/v3.2.1" + "source": "https://github.com/symfony/http-client-contracts/tree/v3.3.0" }, "funding": [ { @@ -7044,32 +7258,34 @@ "type": "tidelift" } ], - "time": "2023-03-01T10:32:47+00:00" + "time": "2023-05-23T14:45:45+00:00" }, { "name": "symfony/http-foundation", - "version": "v6.2.8", + "version": "v6.3.4", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "511a524affeefc191939348823ac75e9921c2112" + "reference": "cac1556fdfdf6719668181974104e6fcfa60e844" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/511a524affeefc191939348823ac75e9921c2112", - "reference": "511a524affeefc191939348823ac75e9921c2112", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/cac1556fdfdf6719668181974104e6fcfa60e844", + "reference": "cac1556fdfdf6719668181974104e6fcfa60e844", "shasum": "" }, "require": { "php": ">=8.1", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-mbstring": "~1.1" + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.1", + "symfony/polyfill-php83": "^1.27" }, "conflict": { "symfony/cache": "<6.2" }, "require-dev": { - "predis/predis": "~1.0", + "doctrine/dbal": "^2.13.1|^3.0", + "predis/predis": "^1.1|^2.0", "symfony/cache": "^5.4|^6.0", "symfony/dependency-injection": "^5.4|^6.0", "symfony/expression-language": "^5.4|^6.0", @@ -7077,9 +7293,6 @@ "symfony/mime": "^5.4|^6.0", "symfony/rate-limiter": "^5.2|^6.0" }, - "suggest": { - "symfony/mime": "To use the file extension guesser" - }, "type": "library", "autoload": { "psr-4": { @@ -7106,7 +7319,7 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v6.2.8" + "source": "https://github.com/symfony/http-foundation/tree/v6.3.4" }, "funding": [ { @@ -7122,29 +7335,29 @@ "type": "tidelift" } ], - "time": "2023-03-29T21:42:15+00:00" + "time": "2023-08-22T08:20:46+00:00" }, { "name": "symfony/http-kernel", - "version": "v6.2.8", + "version": "v6.3.4", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "9563229e56076070d92ca30c089e801e8a4629a3" + "reference": "36abb425b4af863ae1fe54d8a8b8b4c76a2bccdb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/9563229e56076070d92ca30c089e801e8a4629a3", - "reference": "9563229e56076070d92ca30c089e801e8a4629a3", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/36abb425b4af863ae1fe54d8a8b8b4c76a2bccdb", + "reference": "36abb425b4af863ae1fe54d8a8b8b4c76a2bccdb", "shasum": "" }, "require": { "php": ">=8.1", "psr/log": "^1|^2|^3", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/error-handler": "^6.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/error-handler": "^6.3", "symfony/event-dispatcher": "^5.4|^6.0", - "symfony/http-foundation": "^5.4.21|^6.2.7", + "symfony/http-foundation": "^6.3.4", "symfony/polyfill-ctype": "^1.8" }, "conflict": { @@ -7152,15 +7365,18 @@ "symfony/cache": "<5.4", "symfony/config": "<6.1", "symfony/console": "<5.4", - "symfony/dependency-injection": "<6.2", + "symfony/dependency-injection": "<6.3.4", "symfony/doctrine-bridge": "<5.4", "symfony/form": "<5.4", "symfony/http-client": "<5.4", + "symfony/http-client-contracts": "<2.5", "symfony/mailer": "<5.4", "symfony/messenger": "<5.4", "symfony/translation": "<5.4", + "symfony/translation-contracts": "<2.5", "symfony/twig-bridge": "<5.4", "symfony/validator": "<5.4", + "symfony/var-dumper": "<6.3", "twig/twig": "<2.13" }, "provide": { @@ -7169,28 +7385,27 @@ "require-dev": { "psr/cache": "^1.0|^2.0|^3.0", "symfony/browser-kit": "^5.4|^6.0", + "symfony/clock": "^6.2", "symfony/config": "^6.1", "symfony/console": "^5.4|^6.0", "symfony/css-selector": "^5.4|^6.0", - "symfony/dependency-injection": "^6.2", + "symfony/dependency-injection": "^6.3.4", "symfony/dom-crawler": "^5.4|^6.0", "symfony/expression-language": "^5.4|^6.0", "symfony/finder": "^5.4|^6.0", - "symfony/http-client-contracts": "^1.1|^2|^3", + "symfony/http-client-contracts": "^2.5|^3", "symfony/process": "^5.4|^6.0", + "symfony/property-access": "^5.4.5|^6.0.5", "symfony/routing": "^5.4|^6.0", + "symfony/serializer": "^6.3", "symfony/stopwatch": "^5.4|^6.0", "symfony/translation": "^5.4|^6.0", - "symfony/translation-contracts": "^1.1|^2|^3", + "symfony/translation-contracts": "^2.5|^3", "symfony/uid": "^5.4|^6.0", + "symfony/validator": "^6.3", + "symfony/var-exporter": "^6.2", "twig/twig": "^2.13|^3.0.4" }, - "suggest": { - "symfony/browser-kit": "", - "symfony/config": "", - "symfony/console": "", - "symfony/dependency-injection": "" - }, "type": "library", "autoload": { "psr-4": { @@ -7217,7 +7432,7 @@ "description": "Provides a structured process for converting a Request into a Response", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-kernel/tree/v6.2.8" + "source": "https://github.com/symfony/http-kernel/tree/v6.3.4" }, "funding": [ { @@ -7233,20 +7448,20 @@ "type": "tidelift" } ], - "time": "2023-03-31T12:00:10+00:00" + "time": "2023-08-26T13:54:49+00:00" }, { "name": "symfony/mailer", - "version": "v6.2.8", + "version": "v6.3.0", "source": { "type": "git", "url": "https://github.com/symfony/mailer.git", - "reference": "bfcfa015c67e19c6fdb7ca6fe70700af1e740a17" + "reference": "7b03d9be1dea29bfec0a6c7b603f5072a4c97435" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mailer/zipball/bfcfa015c67e19c6fdb7ca6fe70700af1e740a17", - "reference": "bfcfa015c67e19c6fdb7ca6fe70700af1e740a17", + "url": "https://api.github.com/repos/symfony/mailer/zipball/7b03d9be1dea29bfec0a6c7b603f5072a4c97435", + "reference": "7b03d9be1dea29bfec0a6c7b603f5072a4c97435", "shasum": "" }, "require": { @@ -7256,9 +7471,10 @@ "psr/log": "^1|^2|^3", "symfony/event-dispatcher": "^5.4|^6.0", "symfony/mime": "^6.2", - "symfony/service-contracts": "^1.1|^2|^3" + "symfony/service-contracts": "^2.5|^3" }, "conflict": { + "symfony/http-client-contracts": "<2.5", "symfony/http-kernel": "<5.4", "symfony/messenger": "<6.2", "symfony/mime": "<6.2", @@ -7296,7 +7512,7 @@ "description": "Helps sending emails", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/mailer/tree/v6.2.8" + "source": "https://github.com/symfony/mailer/tree/v6.3.0" }, "funding": [ { @@ -7312,24 +7528,25 @@ "type": "tidelift" } ], - "time": "2023-03-14T15:00:05+00:00" + "time": "2023-05-29T12:49:39+00:00" }, { "name": "symfony/mime", - "version": "v6.2.7", + "version": "v6.3.3", "source": { "type": "git", "url": "https://github.com/symfony/mime.git", - "reference": "62e341f80699badb0ad70b31149c8df89a2d778e" + "reference": "9a0cbd52baa5ba5a5b1f0cacc59466f194730f98" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mime/zipball/62e341f80699badb0ad70b31149c8df89a2d778e", - "reference": "62e341f80699badb0ad70b31149c8df89a2d778e", + "url": "https://api.github.com/repos/symfony/mime/zipball/9a0cbd52baa5ba5a5b1f0cacc59466f194730f98", + "reference": "9a0cbd52baa5ba5a5b1f0cacc59466f194730f98", "shasum": "" }, "require": { "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-intl-idn": "^1.10", "symfony/polyfill-mbstring": "^1.0" }, @@ -7338,7 +7555,7 @@ "phpdocumentor/reflection-docblock": "<3.2.2", "phpdocumentor/type-resolver": "<1.4.0", "symfony/mailer": "<5.4", - "symfony/serializer": "<6.2" + "symfony/serializer": "<6.2.13|>=6.3,<6.3.2" }, "require-dev": { "egulias/email-validator": "^2.1.10|^3.1|^4", @@ -7347,7 +7564,7 @@ "symfony/dependency-injection": "^5.4|^6.0", "symfony/property-access": "^5.4|^6.0", "symfony/property-info": "^5.4|^6.0", - "symfony/serializer": "^6.2" + "symfony/serializer": "~6.2.13|^6.3.2" }, "type": "library", "autoload": { @@ -7379,7 +7596,7 @@ "mime-type" ], "support": { - "source": "https://github.com/symfony/mime/tree/v6.2.7" + "source": "https://github.com/symfony/mime/tree/v6.3.3" }, "funding": [ { @@ -7395,25 +7612,25 @@ "type": "tidelift" } ], - "time": "2023-02-24T10:42:00+00:00" + "time": "2023-07-31T07:08:24+00:00" }, { "name": "symfony/options-resolver", - "version": "v6.2.7", + "version": "v6.3.0", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "aa0e85b53bbb2b4951960efd61d295907eacd629" + "reference": "a10f19f5198d589d5c33333cffe98dc9820332dd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/aa0e85b53bbb2b4951960efd61d295907eacd629", - "reference": "aa0e85b53bbb2b4951960efd61d295907eacd629", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/a10f19f5198d589d5c33333cffe98dc9820332dd", + "reference": "a10f19f5198d589d5c33333cffe98dc9820332dd", "shasum": "" }, "require": { "php": ">=8.1", - "symfony/deprecation-contracts": "^2.1|^3" + "symfony/deprecation-contracts": "^2.5|^3" }, "type": "library", "autoload": { @@ -7446,7 +7663,7 @@ "options" ], "support": { - "source": "https://github.com/symfony/options-resolver/tree/v6.2.7" + "source": "https://github.com/symfony/options-resolver/tree/v6.3.0" }, "funding": [ { @@ -7462,20 +7679,20 @@ "type": "tidelift" } ], - "time": "2023-02-14T08:44:56+00:00" + "time": "2023-05-12T14:21:09+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.27.0", + "version": "v1.28.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "5bbc823adecdae860bb64756d639ecfec17b050a" + "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/5bbc823adecdae860bb64756d639ecfec17b050a", - "reference": "5bbc823adecdae860bb64756d639ecfec17b050a", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", + "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", "shasum": "" }, "require": { @@ -7490,7 +7707,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.27-dev" + "dev-main": "1.28-dev" }, "thanks": { "name": "symfony/polyfill", @@ -7528,7 +7745,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.28.0" }, "funding": [ { @@ -7544,20 +7761,20 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2023-01-26T09:26:14+00:00" }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.27.0", + "version": "v1.28.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "511a08c03c1960e08a883f4cffcacd219b758354" + "reference": "875e90aeea2777b6f135677f618529449334a612" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/511a08c03c1960e08a883f4cffcacd219b758354", - "reference": "511a08c03c1960e08a883f4cffcacd219b758354", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/875e90aeea2777b6f135677f618529449334a612", + "reference": "875e90aeea2777b6f135677f618529449334a612", "shasum": "" }, "require": { @@ -7569,7 +7786,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.27-dev" + "dev-main": "1.28-dev" }, "thanks": { "name": "symfony/polyfill", @@ -7609,7 +7826,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.28.0" }, "funding": [ { @@ -7625,20 +7842,20 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2023-01-26T09:26:14+00:00" }, { "name": "symfony/polyfill-intl-idn", - "version": "v1.27.0", + "version": "v1.28.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-idn.git", - "reference": "639084e360537a19f9ee352433b84ce831f3d2da" + "reference": "ecaafce9f77234a6a449d29e49267ba10499116d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/639084e360537a19f9ee352433b84ce831f3d2da", - "reference": "639084e360537a19f9ee352433b84ce831f3d2da", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/ecaafce9f77234a6a449d29e49267ba10499116d", + "reference": "ecaafce9f77234a6a449d29e49267ba10499116d", "shasum": "" }, "require": { @@ -7652,7 +7869,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.27-dev" + "dev-main": "1.28-dev" }, "thanks": { "name": "symfony/polyfill", @@ -7696,7 +7913,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.28.0" }, "funding": [ { @@ -7712,20 +7929,20 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2023-01-26T09:30:37+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.27.0", + "version": "v1.28.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6" + "reference": "8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/19bd1e4fcd5b91116f14d8533c57831ed00571b6", - "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92", + "reference": "8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92", "shasum": "" }, "require": { @@ -7737,7 +7954,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.27-dev" + "dev-main": "1.28-dev" }, "thanks": { "name": "symfony/polyfill", @@ -7780,7 +7997,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.28.0" }, "funding": [ { @@ -7796,20 +8013,20 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2023-01-26T09:26:14+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.27.0", + "version": "v1.28.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534" + "reference": "42292d99c55abe617799667f454222c54c60e229" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534", - "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/42292d99c55abe617799667f454222c54c60e229", + "reference": "42292d99c55abe617799667f454222c54c60e229", "shasum": "" }, "require": { @@ -7824,7 +8041,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.27-dev" + "dev-main": "1.28-dev" }, "thanks": { "name": "symfony/polyfill", @@ -7863,7 +8080,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.28.0" }, "funding": [ { @@ -7879,20 +8096,20 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2023-07-28T09:04:16+00:00" }, { "name": "symfony/polyfill-php72", - "version": "v1.27.0", + "version": "v1.28.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php72.git", - "reference": "869329b1e9894268a8a61dabb69153029b7a8c97" + "reference": "70f4aebd92afca2f865444d30a4d2151c13c3179" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/869329b1e9894268a8a61dabb69153029b7a8c97", - "reference": "869329b1e9894268a8a61dabb69153029b7a8c97", + "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/70f4aebd92afca2f865444d30a4d2151c13c3179", + "reference": "70f4aebd92afca2f865444d30a4d2151c13c3179", "shasum": "" }, "require": { @@ -7901,7 +8118,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.27-dev" + "dev-main": "1.28-dev" }, "thanks": { "name": "symfony/polyfill", @@ -7939,7 +8156,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php72/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-php72/tree/v1.28.0" }, "funding": [ { @@ -7955,20 +8172,20 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2023-01-26T09:26:14+00:00" }, { "name": "symfony/polyfill-php80", - "version": "v1.27.0", + "version": "v1.28.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936" + "reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", - "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/6caa57379c4aec19c0a12a38b59b26487dcfe4b5", + "reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5", "shasum": "" }, "require": { @@ -7977,7 +8194,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.27-dev" + "dev-main": "1.28-dev" }, "thanks": { "name": "symfony/polyfill", @@ -8022,7 +8239,87 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.28.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-01-26T09:26:14+00:00" + }, + { + "name": "symfony/polyfill-php83", + "version": "v1.28.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php83.git", + "reference": "b0f46ebbeeeda3e9d2faebdfbf4b4eae9b59fa11" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/b0f46ebbeeeda3e9d2faebdfbf4b4eae9b59fa11", + "reference": "b0f46ebbeeeda3e9d2faebdfbf4b4eae9b59fa11", + "shasum": "" + }, + "require": { + "php": ">=7.1", + "symfony/polyfill-php80": "^1.14" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.28-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php83\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.3+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php83/tree/v1.28.0" }, "funding": [ { @@ -8038,20 +8335,20 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2023-08-16T06:22:46+00:00" }, { "name": "symfony/polyfill-uuid", - "version": "v1.27.0", + "version": "v1.28.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-uuid.git", - "reference": "f3cf1a645c2734236ed1e2e671e273eeb3586166" + "reference": "9c44518a5aff8da565c8a55dbe85d2769e6f630e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-uuid/zipball/f3cf1a645c2734236ed1e2e671e273eeb3586166", - "reference": "f3cf1a645c2734236ed1e2e671e273eeb3586166", + "url": "https://api.github.com/repos/symfony/polyfill-uuid/zipball/9c44518a5aff8da565c8a55dbe85d2769e6f630e", + "reference": "9c44518a5aff8da565c8a55dbe85d2769e6f630e", "shasum": "" }, "require": { @@ -8066,7 +8363,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.27-dev" + "dev-main": "1.28-dev" }, "thanks": { "name": "symfony/polyfill", @@ -8104,7 +8401,7 @@ "uuid" ], "support": { - "source": "https://github.com/symfony/polyfill-uuid/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-uuid/tree/v1.28.0" }, "funding": [ { @@ -8120,20 +8417,20 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2023-01-26T09:26:14+00:00" }, { "name": "symfony/process", - "version": "v6.2.8", + "version": "v6.3.4", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "75ed64103df4f6615e15a7fe38b8111099f47416" + "reference": "0b5c29118f2e980d455d2e34a5659f4579847c54" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/75ed64103df4f6615e15a7fe38b8111099f47416", - "reference": "75ed64103df4f6615e15a7fe38b8111099f47416", + "url": "https://api.github.com/repos/symfony/process/zipball/0b5c29118f2e980d455d2e34a5659f4579847c54", + "reference": "0b5c29118f2e980d455d2e34a5659f4579847c54", "shasum": "" }, "require": { @@ -8165,7 +8462,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v6.2.8" + "source": "https://github.com/symfony/process/tree/v6.3.4" }, "funding": [ { @@ -8181,36 +8478,37 @@ "type": "tidelift" } ], - "time": "2023-03-09T16:20:02+00:00" + "time": "2023-08-07T10:39:22+00:00" }, { "name": "symfony/psr-http-message-bridge", - "version": "v2.1.4", + "version": "v2.3.1", "source": { "type": "git", "url": "https://github.com/symfony/psr-http-message-bridge.git", - "reference": "a125b93ef378c492e274f217874906fb9babdebb" + "reference": "581ca6067eb62640de5ff08ee1ba6850a0ee472e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/psr-http-message-bridge/zipball/a125b93ef378c492e274f217874906fb9babdebb", - "reference": "a125b93ef378c492e274f217874906fb9babdebb", + "url": "https://api.github.com/repos/symfony/psr-http-message-bridge/zipball/581ca6067eb62640de5ff08ee1ba6850a0ee472e", + "reference": "581ca6067eb62640de5ff08ee1ba6850a0ee472e", "shasum": "" }, "require": { - "php": ">=7.1", - "psr/http-message": "^1.0", - "symfony/http-foundation": "^4.4 || ^5.0 || ^6.0" + "php": ">=7.2.5", + "psr/http-message": "^1.0 || ^2.0", + "symfony/deprecation-contracts": "^2.5 || ^3.0", + "symfony/http-foundation": "^5.4 || ^6.0" }, "require-dev": { "nyholm/psr7": "^1.1", "psr/log": "^1.1 || ^2 || ^3", - "symfony/browser-kit": "^4.4 || ^5.0 || ^6.0", - "symfony/config": "^4.4 || ^5.0 || ^6.0", - "symfony/event-dispatcher": "^4.4 || ^5.0 || ^6.0", - "symfony/framework-bundle": "^4.4 || ^5.0 || ^6.0", - "symfony/http-kernel": "^4.4 || ^5.0 || ^6.0", - "symfony/phpunit-bridge": "^5.4@dev || ^6.0" + "symfony/browser-kit": "^5.4 || ^6.0", + "symfony/config": "^5.4 || ^6.0", + "symfony/event-dispatcher": "^5.4 || ^6.0", + "symfony/framework-bundle": "^5.4 || ^6.0", + "symfony/http-kernel": "^5.4 || ^6.0", + "symfony/phpunit-bridge": "^6.2" }, "suggest": { "nyholm/psr7": "For a super lightweight PSR-7/17 implementation" @@ -8218,7 +8516,7 @@ "type": "symfony-bridge", "extra": { "branch-alias": { - "dev-main": "2.1-dev" + "dev-main": "2.3-dev" } }, "autoload": { @@ -8253,7 +8551,7 @@ ], "support": { "issues": "https://github.com/symfony/psr-http-message-bridge/issues", - "source": "https://github.com/symfony/psr-http-message-bridge/tree/v2.1.4" + "source": "https://github.com/symfony/psr-http-message-bridge/tree/v2.3.1" }, "funding": [ { @@ -8269,24 +8567,25 @@ "type": "tidelift" } ], - "time": "2022-11-28T22:46:34+00:00" + "time": "2023-07-26T11:53:26+00:00" }, { "name": "symfony/routing", - "version": "v6.2.8", + "version": "v6.3.3", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", - "reference": "69062e2823f03b82265d73a966999660f0e1e404" + "reference": "e7243039ab663822ff134fbc46099b5fdfa16f6a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/69062e2823f03b82265d73a966999660f0e1e404", - "reference": "69062e2823f03b82265d73a966999660f0e1e404", + "url": "https://api.github.com/repos/symfony/routing/zipball/e7243039ab663822ff134fbc46099b5fdfa16f6a", + "reference": "e7243039ab663822ff134fbc46099b5fdfa16f6a", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3" }, "conflict": { "doctrine/annotations": "<1.12", @@ -8303,12 +8602,6 @@ "symfony/http-foundation": "^5.4|^6.0", "symfony/yaml": "^5.4|^6.0" }, - "suggest": { - "symfony/config": "For using the all-in-one router or any loader", - "symfony/expression-language": "For using expression matching", - "symfony/http-foundation": "For using a Symfony Request object", - "symfony/yaml": "For using the YAML loader" - }, "type": "library", "autoload": { "psr-4": { @@ -8341,7 +8634,7 @@ "url" ], "support": { - "source": "https://github.com/symfony/routing/tree/v6.2.8" + "source": "https://github.com/symfony/routing/tree/v6.3.3" }, "funding": [ { @@ -8357,20 +8650,20 @@ "type": "tidelift" } ], - "time": "2023-03-14T15:00:05+00:00" + "time": "2023-07-31T07:08:24+00:00" }, { "name": "symfony/service-contracts", - "version": "v3.2.1", + "version": "v3.3.0", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "a8c9cedf55f314f3a186041d19537303766df09a" + "reference": "40da9cc13ec349d9e4966ce18b5fbcd724ab10a4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/a8c9cedf55f314f3a186041d19537303766df09a", - "reference": "a8c9cedf55f314f3a186041d19537303766df09a", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/40da9cc13ec349d9e4966ce18b5fbcd724ab10a4", + "reference": "40da9cc13ec349d9e4966ce18b5fbcd724ab10a4", "shasum": "" }, "require": { @@ -8380,13 +8673,10 @@ "conflict": { "ext-psr": "<1.1|>=2" }, - "suggest": { - "symfony/service-implementation": "" - }, "type": "library", "extra": { "branch-alias": { - "dev-main": "3.3-dev" + "dev-main": "3.4-dev" }, "thanks": { "name": "symfony/contracts", @@ -8426,7 +8716,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.2.1" + "source": "https://github.com/symfony/service-contracts/tree/v3.3.0" }, "funding": [ { @@ -8442,20 +8732,20 @@ "type": "tidelift" } ], - "time": "2023-03-01T10:32:47+00:00" + "time": "2023-05-23T14:45:45+00:00" }, { "name": "symfony/string", - "version": "v6.2.8", + "version": "v6.3.2", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "193e83bbd6617d6b2151c37fff10fa7168ebddef" + "reference": "53d1a83225002635bca3482fcbf963001313fb68" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/193e83bbd6617d6b2151c37fff10fa7168ebddef", - "reference": "193e83bbd6617d6b2151c37fff10fa7168ebddef", + "url": "https://api.github.com/repos/symfony/string/zipball/53d1a83225002635bca3482fcbf963001313fb68", + "reference": "53d1a83225002635bca3482fcbf963001313fb68", "shasum": "" }, "require": { @@ -8466,13 +8756,13 @@ "symfony/polyfill-mbstring": "~1.0" }, "conflict": { - "symfony/translation-contracts": "<2.0" + "symfony/translation-contracts": "<2.5" }, "require-dev": { "symfony/error-handler": "^5.4|^6.0", "symfony/http-client": "^5.4|^6.0", "symfony/intl": "^6.2", - "symfony/translation-contracts": "^2.0|^3.0", + "symfony/translation-contracts": "^2.5|^3.0", "symfony/var-exporter": "^5.4|^6.0" }, "type": "library", @@ -8512,7 +8802,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v6.2.8" + "source": "https://github.com/symfony/string/tree/v6.3.2" }, "funding": [ { @@ -8528,32 +8818,35 @@ "type": "tidelift" } ], - "time": "2023-03-20T16:06:02+00:00" + "time": "2023-07-05T08:41:27+00:00" }, { "name": "symfony/translation", - "version": "v6.2.8", + "version": "v6.3.3", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "817535dbb1721df8b3a8f2489dc7e50bcd6209b5" + "reference": "3ed078c54bc98bbe4414e1e9b2d5e85ed5a5c8bd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/817535dbb1721df8b3a8f2489dc7e50bcd6209b5", - "reference": "817535dbb1721df8b3a8f2489dc7e50bcd6209b5", + "url": "https://api.github.com/repos/symfony/translation/zipball/3ed078c54bc98bbe4414e1e9b2d5e85ed5a5c8bd", + "reference": "3ed078c54bc98bbe4414e1e9b2d5e85ed5a5c8bd", "shasum": "" }, "require": { "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-mbstring": "~1.0", - "symfony/translation-contracts": "^2.3|^3.0" + "symfony/translation-contracts": "^2.5|^3.0" }, "conflict": { "symfony/config": "<5.4", "symfony/console": "<5.4", "symfony/dependency-injection": "<5.4", + "symfony/http-client-contracts": "<2.5", "symfony/http-kernel": "<5.4", + "symfony/service-contracts": "<2.5", "symfony/twig-bundle": "<5.4", "symfony/yaml": "<5.4" }, @@ -8567,20 +8860,14 @@ "symfony/console": "^5.4|^6.0", "symfony/dependency-injection": "^5.4|^6.0", "symfony/finder": "^5.4|^6.0", - "symfony/http-client-contracts": "^1.1|^2.0|^3.0", + "symfony/http-client-contracts": "^2.5|^3.0", "symfony/http-kernel": "^5.4|^6.0", "symfony/intl": "^5.4|^6.0", "symfony/polyfill-intl-icu": "^1.21", "symfony/routing": "^5.4|^6.0", - "symfony/service-contracts": "^1.1.2|^2|^3", + "symfony/service-contracts": "^2.5|^3", "symfony/yaml": "^5.4|^6.0" }, - "suggest": { - "nikic/php-parser": "To use PhpAstExtractor", - "psr/log-implementation": "To use logging capability in translator", - "symfony/config": "", - "symfony/yaml": "" - }, "type": "library", "autoload": { "files": [ @@ -8610,7 +8897,7 @@ "description": "Provides tools to internationalize your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/translation/tree/v6.2.8" + "source": "https://github.com/symfony/translation/tree/v6.3.3" }, "funding": [ { @@ -8626,32 +8913,29 @@ "type": "tidelift" } ], - "time": "2023-03-31T09:14:44+00:00" + "time": "2023-07-31T07:08:24+00:00" }, { "name": "symfony/translation-contracts", - "version": "v3.2.1", + "version": "v3.3.0", "source": { "type": "git", "url": "https://github.com/symfony/translation-contracts.git", - "reference": "dfec258b9dd17a6b24420d464c43bffe347441c8" + "reference": "02c24deb352fb0d79db5486c0c79905a85e37e86" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/dfec258b9dd17a6b24420d464c43bffe347441c8", - "reference": "dfec258b9dd17a6b24420d464c43bffe347441c8", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/02c24deb352fb0d79db5486c0c79905a85e37e86", + "reference": "02c24deb352fb0d79db5486c0c79905a85e37e86", "shasum": "" }, "require": { "php": ">=8.1" }, - "suggest": { - "symfony/translation-implementation": "" - }, "type": "library", "extra": { "branch-alias": { - "dev-main": "3.3-dev" + "dev-main": "3.4-dev" }, "thanks": { "name": "symfony/contracts", @@ -8691,7 +8975,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/translation-contracts/tree/v3.2.1" + "source": "https://github.com/symfony/translation-contracts/tree/v3.3.0" }, "funding": [ { @@ -8707,20 +8991,20 @@ "type": "tidelift" } ], - "time": "2023-03-01T10:32:47+00:00" + "time": "2023-05-30T17:17:10+00:00" }, { "name": "symfony/uid", - "version": "v6.2.7", + "version": "v6.3.0", "source": { "type": "git", "url": "https://github.com/symfony/uid.git", - "reference": "d30c72a63897cfa043e1de4d4dd2ffa9ecefcdc0" + "reference": "01b0f20b1351d997711c56f1638f7a8c3061e384" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/uid/zipball/d30c72a63897cfa043e1de4d4dd2ffa9ecefcdc0", - "reference": "d30c72a63897cfa043e1de4d4dd2ffa9ecefcdc0", + "url": "https://api.github.com/repos/symfony/uid/zipball/01b0f20b1351d997711c56f1638f7a8c3061e384", + "reference": "01b0f20b1351d997711c56f1638f7a8c3061e384", "shasum": "" }, "require": { @@ -8765,7 +9049,7 @@ "uuid" ], "support": { - "source": "https://github.com/symfony/uid/tree/v6.2.7" + "source": "https://github.com/symfony/uid/tree/v6.3.0" }, "funding": [ { @@ -8781,42 +9065,38 @@ "type": "tidelift" } ], - "time": "2023-02-14T08:44:56+00:00" + "time": "2023-04-08T07:25:02+00:00" }, { "name": "symfony/var-dumper", - "version": "v6.2.8", + "version": "v6.3.4", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "d37ab6787be2db993747b6218fcc96e8e3bb4bd0" + "reference": "2027be14f8ae8eae999ceadebcda5b4909b81d45" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/d37ab6787be2db993747b6218fcc96e8e3bb4bd0", - "reference": "d37ab6787be2db993747b6218fcc96e8e3bb4bd0", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/2027be14f8ae8eae999ceadebcda5b4909b81d45", + "reference": "2027be14f8ae8eae999ceadebcda5b4909b81d45", "shasum": "" }, "require": { "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-mbstring": "~1.0" }, "conflict": { - "phpunit/phpunit": "<5.4.3", "symfony/console": "<5.4" }, "require-dev": { "ext-iconv": "*", "symfony/console": "^5.4|^6.0", + "symfony/http-kernel": "^5.4|^6.0", "symfony/process": "^5.4|^6.0", "symfony/uid": "^5.4|^6.0", "twig/twig": "^2.13|^3.0.4" }, - "suggest": { - "ext-iconv": "To convert non-UTF-8 strings to UTF-8 (or symfony/polyfill-iconv in case ext-iconv cannot be used).", - "ext-intl": "To show region name in time zone dump", - "symfony/console": "To use the ServerDumpCommand and/or the bin/var-dump-server script" - }, "bin": [ "Resources/bin/var-dump-server" ], @@ -8853,7 +9133,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v6.2.8" + "source": "https://github.com/symfony/var-dumper/tree/v6.3.4" }, "funding": [ { @@ -8869,7 +9149,7 @@ "type": "tidelift" } ], - "time": "2023-03-29T21:42:15+00:00" + "time": "2023-08-24T14:51:05+00:00" }, { "name": "tgalopin/html-sanitizer", @@ -9313,16 +9593,16 @@ }, { "name": "barryvdh/laravel-debugbar", - "version": "v3.8.1", + "version": "v3.9.2", "source": { "type": "git", "url": "https://github.com/barryvdh/laravel-debugbar.git", - "reference": "aff3235fecb4104203b1e62c32239c56530eee32" + "reference": "bfd0131c146973cab164e50f5cdd8a67cc60cab1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/barryvdh/laravel-debugbar/zipball/aff3235fecb4104203b1e62c32239c56530eee32", - "reference": "aff3235fecb4104203b1e62c32239c56530eee32", + "url": "https://api.github.com/repos/barryvdh/laravel-debugbar/zipball/bfd0131c146973cab164e50f5cdd8a67cc60cab1", + "reference": "bfd0131c146973cab164e50f5cdd8a67cc60cab1", "shasum": "" }, "require": { @@ -9381,7 +9661,7 @@ ], "support": { "issues": "https://github.com/barryvdh/laravel-debugbar/issues", - "source": "https://github.com/barryvdh/laravel-debugbar/tree/v3.8.1" + "source": "https://github.com/barryvdh/laravel-debugbar/tree/v3.9.2" }, "funding": [ { @@ -9393,7 +9673,7 @@ "type": "github" } ], - "time": "2023-02-21T14:21:02+00:00" + "time": "2023-08-25T18:43:57+00:00" }, { "name": "barryvdh/laravel-ide-helper", @@ -9782,21 +10062,21 @@ }, { "name": "dragon-code/contracts", - "version": "v2.19.0", + "version": "v2.20.0", "source": { "type": "git", "url": "https://github.com/TheDragonCode/contracts.git", - "reference": "b50ceb575da285c68615bf759d2bb3288aaa4b25" + "reference": "410d35bec404f465a126bb1e672f3abe1150f5d7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/TheDragonCode/contracts/zipball/b50ceb575da285c68615bf759d2bb3288aaa4b25", - "reference": "b50ceb575da285c68615bf759d2bb3288aaa4b25", + "url": "https://api.github.com/repos/TheDragonCode/contracts/zipball/410d35bec404f465a126bb1e672f3abe1150f5d7", + "reference": "410d35bec404f465a126bb1e672f3abe1150f5d7", "shasum": "" }, "require": { "php": "^7.2.5 || ^8.0", - "psr/http-message": "^1.0.1", + "psr/http-message": "^1.0.1 || ^2.0", "symfony/http-kernel": "^4.0 || ^5.0 || ^6.0", "symfony/polyfill-php80": "^1.23" }, @@ -9804,7 +10084,7 @@ "andrey-helldar/contracts": "*" }, "require-dev": { - "illuminate/database": "^8.0", + "illuminate/database": "^10.0", "phpdocumentor/reflection-docblock": "^5.0" }, "type": "library", @@ -9850,31 +10130,31 @@ "type": "yoomoney" } ], - "time": "2022-10-10T22:02:52+00:00" + "time": "2023-06-02T11:06:31+00:00" }, { "name": "dragon-code/pretty-array", - "version": "v4.0.0", + "version": "v4.1.0", "source": { "type": "git", "url": "https://github.com/TheDragonCode/pretty-array.git", - "reference": "385ca81ee7e9a65fb5696692c173a1234fc2ad7d" + "reference": "6c84e2454491b414efbd37985c322712cdf9012f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/TheDragonCode/pretty-array/zipball/385ca81ee7e9a65fb5696692c173a1234fc2ad7d", - "reference": "385ca81ee7e9a65fb5696692c173a1234fc2ad7d", + "url": "https://api.github.com/repos/TheDragonCode/pretty-array/zipball/6c84e2454491b414efbd37985c322712cdf9012f", + "reference": "6c84e2454491b414efbd37985c322712cdf9012f", "shasum": "" }, "require": { - "dragon-code/contracts": "^2.6", - "dragon-code/support": "^6.0", + "dragon-code/contracts": "^2.20", + "dragon-code/support": "^6.11.2", "ext-dom": "*", "ext-mbstring": "*", "php": "^8.0" }, "require-dev": { - "phpunit/phpunit": "^9.5" + "phpunit/phpunit": "^9.6 || ^10.2" }, "suggest": { "symfony/thanks": "Give thanks (in the form of a GitHub) to your fellow PHP package maintainers" @@ -9892,7 +10172,8 @@ "authors": [ { "name": "Andrey Helldar", - "email": "helldar@ai-rus.com" + "email": "helldar@dragon-code.pro", + "homepage": "https://github.com/andrey-helldar" } ], "description": "Simple conversion of an array to a pretty view", @@ -9910,15 +10191,11 @@ }, "funding": [ { - "url": "https://paypal.me/helldar", - "type": "custom" - }, - { - "url": "https://yoomoney.ru/to/410012608840929", - "type": "custom" + "url": "https://boosty.to/dragon-code", + "type": "boosty" }, { - "url": "https://github.com/TheDragonCode", + "url": "https://github.com/sponsors/TheDragonCode", "type": "github" }, { @@ -9926,35 +10203,35 @@ "type": "open_collective" }, { - "url": "https://www.patreon.com/andrey_helldar", - "type": "patreon" + "url": "https://yoomoney.ru/to/410012608840929", + "type": "yoomoney" } ], - "time": "2022-04-21T12:22:41+00:00" + "time": "2023-06-02T11:37:44+00:00" }, { "name": "dragon-code/support", - "version": "v6.11.1", + "version": "v6.11.3", "source": { "type": "git", "url": "https://github.com/TheDragonCode/support.git", - "reference": "0560de8ae9b2641a1c8f27ebc1d602ea425153c3" + "reference": "3dbf1715e83e216ae1fea31eecc022829dc864df" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/TheDragonCode/support/zipball/0560de8ae9b2641a1c8f27ebc1d602ea425153c3", - "reference": "0560de8ae9b2641a1c8f27ebc1d602ea425153c3", + "url": "https://api.github.com/repos/TheDragonCode/support/zipball/3dbf1715e83e216ae1fea31eecc022829dc864df", + "reference": "3dbf1715e83e216ae1fea31eecc022829dc864df", "shasum": "" }, "require": { - "dragon-code/contracts": "^2.18", + "dragon-code/contracts": "^2.19.1", "ext-bcmath": "*", "ext-ctype": "*", "ext-dom": "*", "ext-json": "*", "ext-mbstring": "*", "php": "^8.0", - "psr/http-message": "^1.0.1", + "psr/http-message": "^1.0.1 || ^2.0", "symfony/polyfill-php81": "^1.25", "voku/portable-ascii": "^1.4.8 || ^2.0.1" }, @@ -10020,6 +10297,10 @@ "url": "https://boosty.to/dragon-code", "type": "boosty" }, + { + "url": "https://www.donationalerts.com/r/dragon_code", + "type": "donationalerts" + }, { "url": "https://github.com/sponsors/TheDragonCode", "type": "github" @@ -10033,7 +10314,7 @@ "type": "yoomoney" } ], - "time": "2023-04-01T12:41:34+00:00" + "time": "2023-08-03T09:43:52+00:00" }, { "name": "fakerphp/faker", @@ -10288,16 +10569,16 @@ }, { "name": "laravel-lang/lang", - "version": "12.19.2", + "version": "13.2.2", "source": { "type": "git", "url": "https://github.com/Laravel-Lang/lang.git", - "reference": "68554447439dfd56193eccb9fd3bbc2bb520cc6a" + "reference": "bbef7ad051664626373ab68628676f140b2ec733" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Laravel-Lang/lang/zipball/68554447439dfd56193eccb9fd3bbc2bb520cc6a", - "reference": "68554447439dfd56193eccb9fd3bbc2bb520cc6a", + "url": "https://api.github.com/repos/Laravel-Lang/lang/zipball/bbef7ad051664626373ab68628676f140b2ec733", + "reference": "bbef7ad051664626373ab68628676f140b2ec733", "shasum": "" }, "require": { @@ -10306,8 +10587,8 @@ "php": "^8.1" }, "require-dev": { - "laravel-lang/status-generator": "^1.13", - "phpunit/phpunit": "^9.6", + "laravel-lang/status-generator": "^1.19", + "phpunit/phpunit": "^10.0", "symfony/var-dumper": "^6.0" }, "type": "library", @@ -10350,20 +10631,20 @@ "type": "open_collective" } ], - "time": "2023-04-02T17:15:27+00:00" + "time": "2023-08-26T02:07:55+00:00" }, { "name": "laravel-lang/publisher", - "version": "v14.6.4", + "version": "v14.7.0", "source": { "type": "git", "url": "https://github.com/Laravel-Lang/publisher.git", - "reference": "a6df495f4c8bb7e8555cfbf82070bd417fbef9bb" + "reference": "4e6fa2d8c446282b832ffb16b9b3c33aa4296a5b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Laravel-Lang/publisher/zipball/a6df495f4c8bb7e8555cfbf82070bd417fbef9bb", - "reference": "a6df495f4c8bb7e8555cfbf82070bd417fbef9bb", + "url": "https://api.github.com/repos/Laravel-Lang/publisher/zipball/4e6fa2d8c446282b832ffb16b9b3c33aa4296a5b", + "reference": "4e6fa2d8c446282b832ffb16b9b3c33aa4296a5b", "shasum": "" }, "require": { @@ -10372,8 +10653,8 @@ "dragon-code/pretty-array": "^4.0", "dragon-code/support": "^6.3", "ext-json": "*", - "illuminate/console": "^8.79 || ^9.18 || ^10.0", - "illuminate/support": "^8.79 || ^9.18 || ^10.0", + "illuminate/console": "^8.79 || ^9.18 || ^10.0 || ^11.0", + "illuminate/support": "^8.79 || ^9.18 || ^10.0 || ^11.0", "league/commonmark": "^2.3", "league/config": "^1.2", "php": "^8.1" @@ -10385,8 +10666,8 @@ }, "require-dev": { "laravel-lang/json-fallback-hotfix": "^1.0", - "orchestra/testbench": "^6.25 || ^7.22 || ^8.0", - "phpunit/phpunit": "^9.6", + "orchestra/testbench": "^6.25 || ^7.22 || ^8.0 || ^9.0", + "phpunit/phpunit": "^9.6 || ^10.0", "symfony/var-dumper": "^5.0 || ^6.0" }, "suggest": { @@ -10457,20 +10738,20 @@ "type": "open_collective" } ], - "time": "2023-03-31T14:18:17+00:00" + "time": "2023-08-09T09:09:57+00:00" }, { "name": "laravel/pint", - "version": "v1.7.0", + "version": "v1.13.1", "source": { "type": "git", "url": "https://github.com/laravel/pint.git", - "reference": "d55381c73b7308e1b8a124084e804193a179092e" + "reference": "22f204242d68095b3ba7dab5d3ef0240454a4652" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/pint/zipball/d55381c73b7308e1b8a124084e804193a179092e", - "reference": "d55381c73b7308e1b8a124084e804193a179092e", + "url": "https://api.github.com/repos/laravel/pint/zipball/22f204242d68095b3ba7dab5d3ef0240454a4652", + "reference": "22f204242d68095b3ba7dab5d3ef0240454a4652", "shasum": "" }, "require": { @@ -10481,13 +10762,13 @@ "php": "^8.1.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^3.14.4", - "illuminate/view": "^10.0.0", - "laravel-zero/framework": "^10.0.0", + "friendsofphp/php-cs-fixer": "^3.21.1", + "illuminate/view": "^10.5.1", + "laravel-zero/framework": "^10.1.2", "mockery/mockery": "^1.5.1", - "nunomaduro/larastan": "^2.4.0", + "nunomaduro/larastan": "^2.5.1", "nunomaduro/termwind": "^1.15.1", - "pestphp/pest": "^1.22.4" + "pestphp/pest": "^2.4.0" }, "bin": [ "builds/pint" @@ -10523,7 +10804,7 @@ "issues": "https://github.com/laravel/pint/issues", "source": "https://github.com/laravel/pint" }, - "time": "2023-03-21T10:55:35+00:00" + "time": "2023-09-06T11:03:34+00:00" }, { "name": "laravel/sail", @@ -10658,38 +10939,40 @@ }, { "name": "mockery/mockery", - "version": "1.5.1", + "version": "1.6.6", "source": { "type": "git", "url": "https://github.com/mockery/mockery.git", - "reference": "e92dcc83d5a51851baf5f5591d32cb2b16e3684e" + "reference": "b8e0bb7d8c604046539c1115994632c74dcb361e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/mockery/mockery/zipball/e92dcc83d5a51851baf5f5591d32cb2b16e3684e", - "reference": "e92dcc83d5a51851baf5f5591d32cb2b16e3684e", + "url": "https://api.github.com/repos/mockery/mockery/zipball/b8e0bb7d8c604046539c1115994632c74dcb361e", + "reference": "b8e0bb7d8c604046539c1115994632c74dcb361e", "shasum": "" }, "require": { "hamcrest/hamcrest-php": "^2.0.1", "lib-pcre": ">=7.0", - "php": "^7.3 || ^8.0" + "php": ">=7.3" }, "conflict": { "phpunit/phpunit": "<8.0" }, "require-dev": { - "phpunit/phpunit": "^8.5 || ^9.3" + "phpunit/phpunit": "^8.5 || ^9.6.10", + "psalm/plugin-phpunit": "^0.18.4", + "symplify/easy-coding-standard": "^11.5.0", + "vimeo/psalm": "^4.30" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.4.x-dev" - } - }, "autoload": { - "psr-0": { - "Mockery": "library/" + "files": [ + "library/helpers.php", + "library/Mockery.php" + ], + "psr-4": { + "Mockery\\": "library/Mockery" } }, "notification-url": "https://packagist.org/downloads/", @@ -10700,12 +10983,20 @@ { "name": "Pádraic Brady", "email": "padraic.brady@gmail.com", - "homepage": "http://blog.astrumfutura.com" + "homepage": "https://github.com/padraic", + "role": "Author" }, { "name": "Dave Marshall", "email": "dave.marshall@atstsolutions.co.uk", - "homepage": "http://davedevelopment.co.uk" + "homepage": "https://davedevelopment.co.uk", + "role": "Developer" + }, + { + "name": "Nathanael Esayeas", + "email": "nathanael.esayeas@protonmail.com", + "homepage": "https://github.com/ghostwriter", + "role": "Lead Developer" } ], "description": "Mockery is a simple yet flexible PHP mock object framework", @@ -10723,10 +11014,13 @@ "testing" ], "support": { + "docs": "https://docs.mockery.io/", "issues": "https://github.com/mockery/mockery/issues", - "source": "https://github.com/mockery/mockery/tree/1.5.1" + "rss": "https://github.com/mockery/mockery/releases.atom", + "security": "https://github.com/mockery/mockery/security/advisories", + "source": "https://github.com/mockery/mockery" }, - "time": "2022-09-07T15:32:08+00:00" + "time": "2023-08-09T00:03:52+00:00" }, { "name": "myclabs/deep-copy", @@ -11473,16 +11767,16 @@ }, { "name": "phpdocumentor/type-resolver", - "version": "1.7.1", + "version": "1.7.3", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "dfc078e8af9c99210337325ff5aa152872c98714" + "reference": "3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/dfc078e8af9c99210337325ff5aa152872c98714", - "reference": "dfc078e8af9c99210337325ff5aa152872c98714", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419", + "reference": "3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419", "shasum": "" }, "require": { @@ -11525,28 +11819,30 @@ "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", "support": { "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.7.1" + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.7.3" }, - "time": "2023-03-27T19:02:04+00:00" + "time": "2023-08-12T11:01:26+00:00" }, { "name": "phpstan/phpdoc-parser", - "version": "1.16.1", + "version": "1.24.0", "source": { "type": "git", "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "e27e92d939e2e3636f0a1f0afaba59692c0bf571" + "reference": "3510b0a6274cc42f7219367cb3abfc123ffa09d6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/e27e92d939e2e3636f0a1f0afaba59692c0bf571", - "reference": "e27e92d939e2e3636f0a1f0afaba59692c0bf571", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/3510b0a6274cc42f7219367cb3abfc123ffa09d6", + "reference": "3510b0a6274cc42f7219367cb3abfc123ffa09d6", "shasum": "" }, "require": { "php": "^7.2 || ^8.0" }, "require-dev": { + "doctrine/annotations": "^2.0", + "nikic/php-parser": "^4.15", "php-parallel-lint/php-parallel-lint": "^1.2", "phpstan/extension-installer": "^1.0", "phpstan/phpstan": "^1.5", @@ -11570,9 +11866,9 @@ "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/1.16.1" + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.24.0" }, - "time": "2023-02-07T18:11:17+00:00" + "time": "2023-09-07T20:46:32+00:00" }, { "name": "phpunit/php-code-coverage", @@ -13198,16 +13494,16 @@ }, { "name": "symfony/polyfill-php81", - "version": "v1.27.0", + "version": "v1.28.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php81.git", - "reference": "707403074c8ea6e2edaf8794b0157a0bfa52157a" + "reference": "7581cd600fa9fd681b797d00b02f068e2f13263b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/707403074c8ea6e2edaf8794b0157a0bfa52157a", - "reference": "707403074c8ea6e2edaf8794b0157a0bfa52157a", + "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/7581cd600fa9fd681b797d00b02f068e2f13263b", + "reference": "7581cd600fa9fd681b797d00b02f068e2f13263b", "shasum": "" }, "require": { @@ -13216,7 +13512,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.27-dev" + "dev-main": "1.28-dev" }, "thanks": { "name": "symfony/polyfill", @@ -13257,7 +13553,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php81/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-php81/tree/v1.28.0" }, "funding": [ { @@ -13273,7 +13569,7 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2023-01-26T09:26:14+00:00" }, { "name": "symfony/yaml", @@ -13467,8 +13763,9 @@ "prefer-lowest": false, "platform": { "php": "^8.1", + "ext-grpc": "*", "ext-json": "*" }, "platform-dev": [], - "plugin-api-version": "2.3.0" + "plugin-api-version": "2.6.0" } diff --git a/config/discord.php b/config/discord.php index 8d1e3953..28027ba8 100644 --- a/config/discord.php +++ b/config/discord.php @@ -5,5 +5,9 @@ 'avatar_url' => env('DISCORD_AVATAR_URL', sprintf('%s/images/logo.png', env('APP_URL'))), 'enabled' => env('DISCORD_NOTIFICATIONS_ENABLE', false), 'webhook_url' => env('DISCORD_WEBHOOK_URL', ''), - 'token' => env('DISCORD_AUTH_TOKEN', '') + 'token' => env('DISCORD_AUTH_TOKEN', ''), + 'service_host' => env('DISCORD_BOT_SERVICE_URL', 'localhost'), + 'service_token' => env('DISCORD_BOT_JWT', ''), + 'ecfmp_channel_id' => env('DISCORD_ECFMP_CHANNEL_ID', ''), + 'client_request_app_id' => env('DISCORD_CLIENT_REQUEST_APP_ID', ''), ]; diff --git a/database/factories/DiscordNotificationFactory.php b/database/factories/DiscordNotificationFactory.php index 708a73ad..1d3eb03a 100644 --- a/database/factories/DiscordNotificationFactory.php +++ b/database/factories/DiscordNotificationFactory.php @@ -2,7 +2,6 @@ namespace Database\Factories; -use App\Models\DivisionDiscordWebhook; use Illuminate\Database\Eloquent\Factories\Factory; /** @@ -18,18 +17,7 @@ class DiscordNotificationFactory extends Factory public function definition() { return [ - 'division_discord_webhook_id' => null, - 'content' => 'ohai', - 'embeds' => [ - 'foo' => 'var', - ], + 'remote_id' => $this->faker->uuid, ]; } - - public function toDivisionWebhook(DivisionDiscordWebhook $divisionDiscordWebhook): static - { - return $this->state(fn (array $attributes) => [ - 'division_discord_webhook_id' => $divisionDiscordWebhook->id, - ]); - } } diff --git a/database/factories/DivisionDiscordNotificationFactory.php b/database/factories/DivisionDiscordNotificationFactory.php new file mode 100644 index 00000000..58dc4a2b --- /dev/null +++ b/database/factories/DivisionDiscordNotificationFactory.php @@ -0,0 +1,35 @@ + + */ +class DivisionDiscordNotificationFactory extends Factory +{ + /** + * Define the model's default state. + * + * @return array + */ + public function definition() + { + return [ + 'division_discord_webhook_id' => null, + 'content' => 'ohai', + 'embeds' => [ + 'foo' => 'var', + ], + ]; + } + + public function toDivisionWebhook(DivisionDiscordWebhook $divisionDiscordWebhook): static + { + return $this->state(fn (array $attributes) => [ + 'division_discord_webhook_id' => $divisionDiscordWebhook->id, + ]); + } +} diff --git a/database/factories/FlowMeasureFactory.php b/database/factories/FlowMeasureFactory.php index 0942e40b..488b23f2 100644 --- a/database/factories/FlowMeasureFactory.php +++ b/database/factories/FlowMeasureFactory.php @@ -21,9 +21,12 @@ public function definition() { $fir = FlightInformationRegion::factory()->create(); $startDate = Carbon::parse($this->faker->dateTimeBetween('-1 hour')); + $identifier = FlowMeasureIdentifierGenerator::generateIdentifier($startDate, $fir); return [ - 'identifier' => FlowMeasureIdentifierGenerator::generateIdentifier($startDate, $fir), + 'identifier' => $identifier, + 'canonical_identifier' => FlowMeasureIdentifierGenerator::canonicalIdentifier($identifier), + 'revision_number' => FlowMeasureIdentifierGenerator::timesRevised($identifier), 'user_id' => User::factory()->create()->id, 'flight_information_region_id' => $fir->id, 'event_id' => null, @@ -102,6 +105,14 @@ public function notNotified(): static ]); } + + public function withdrawn(): static + { + return $this->state(fn (array $attributes) => [ + 'deleted_at' => Carbon::now(), + ]); + } + public function withEvent(): static { return $this->state(fn (array $attributes) => [ diff --git a/database/migrations/2023_10_08_112953_rename_discord_notifications_tables.php b/database/migrations/2023_10_08_112953_rename_discord_notifications_tables.php new file mode 100644 index 00000000..b4e3faa1 --- /dev/null +++ b/database/migrations/2023_10_08_112953_rename_discord_notifications_tables.php @@ -0,0 +1,22 @@ +id(); + $table->uuid('remote_id') + ->unique(); + $table->timestamp('created_at'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('discord_notifications'); + } +}; diff --git a/database/migrations/2023_10_08_123708_create_discord_notification_flow_measure_table.php b/database/migrations/2023_10_08_123708_create_discord_notification_flow_measure_table.php new file mode 100644 index 00000000..6bf85cc0 --- /dev/null +++ b/database/migrations/2023_10_08_123708_create_discord_notification_flow_measure_table.php @@ -0,0 +1,44 @@ +id(); + + $table->foreignIdFor(FlowMeasure::class) + ->constrained() + ->cascadeOnDelete(); + + $table->foreignId('discord_notification_id') + ->constrained('discord_notifications', indexName: 'discord_notification_id') + ->cascadeOnDelete(); + + $table->foreignId('discord_notification_type_id') + ->constrained('discord_notification_types', indexName: 'discord_notification_type_id'); + + $table->string('notified_as') + ->index() + ->comment('The identifier of the flow measure at the point of notification') + ->cascadeOnDelete(); + + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('discord_notification_flow_measure'); + } +}; diff --git a/database/migrations/2023_10_14_150204_add_columns_to_flow_measures_table.php b/database/migrations/2023_10_14_150204_add_columns_to_flow_measures_table.php new file mode 100644 index 00000000..2103467f --- /dev/null +++ b/database/migrations/2023_10_14_150204_add_columns_to_flow_measures_table.php @@ -0,0 +1,34 @@ +string('canonical_identifier') + ->after('identifier') + ->comment('The original identifier of the flow measure'); + + $table->unsignedInteger('revision_number') + ->after('canonical_identifier') + ->comment('The revision number of the flow measure'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('flow_measures', function (Blueprint $table) { + $table->dropColumn('revision_number'); + $table->dropColumn('canonical_identifier'); + }); + } +}; diff --git a/database/migrations/2023_10_14_150428_update_flow_measure_data.php b/database/migrations/2023_10_14_150428_update_flow_measure_data.php new file mode 100644 index 00000000..b9a324df --- /dev/null +++ b/database/migrations/2023_10_14_150428_update_flow_measure_data.php @@ -0,0 +1,27 @@ +each(function (FlowMeasure $flowMeasure) { + $flowMeasure->revision_number = FlowMeasureIdentifierGenerator::timesRevised($flowMeasure); + $flowMeasure->canonical_identifier = FlowMeasureIdentifierGenerator::canonicalIdentifier($flowMeasure); + $flowMeasure->save(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + // + } +}; diff --git a/database/schema/mysql-schema.sql b/database/schema/mysql-schema.sql new file mode 100644 index 00000000..cbca8ab7 --- /dev/null +++ b/database/schema/mysql-schema.sql @@ -0,0 +1,726 @@ +-- MySQL dump 10.13 Distrib 8.0.34, for Linux (x86_64) +-- +-- Host: mysql Database: laravel +-- ------------------------------------------------------ +-- Server version 8.0.32 + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!50503 SET NAMES utf8mb4 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `activity_log` +-- + +DROP TABLE IF EXISTS `activity_log`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `activity_log` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT, + `log_name` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `description` text COLLATE utf8mb4_unicode_ci NOT NULL, + `subject_type` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `event` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `subject_id` bigint unsigned DEFAULT NULL, + `causer_type` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `causer_id` bigint unsigned DEFAULT NULL, + `properties` json DEFAULT NULL, + `batch_uuid` char(36) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `subject` (`subject_type`,`subject_id`), + KEY `causer` (`causer_type`,`causer_id`), + KEY `activity_log_log_name_index` (`log_name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `activity_log` +-- + +LOCK TABLES `activity_log` WRITE; +/*!40000 ALTER TABLE `activity_log` DISABLE KEYS */; +/*!40000 ALTER TABLE `activity_log` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `airport_airport_group` +-- + +DROP TABLE IF EXISTS `airport_airport_group`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `airport_airport_group` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT, + `airport_id` bigint unsigned NOT NULL, + `airport_group_id` bigint unsigned NOT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `airport_airport_group_id` (`airport_id`,`airport_group_id`), + KEY `airport_airport_group_airport_group_id_foreign` (`airport_group_id`), + CONSTRAINT `airport_airport_group_airport_group_id_foreign` FOREIGN KEY (`airport_group_id`) REFERENCES `airport_groups` (`id`) ON DELETE CASCADE, + CONSTRAINT `airport_airport_group_airport_id_foreign` FOREIGN KEY (`airport_id`) REFERENCES `airports` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `airport_airport_group` +-- + +LOCK TABLES `airport_airport_group` WRITE; +/*!40000 ALTER TABLE `airport_airport_group` DISABLE KEYS */; +/*!40000 ALTER TABLE `airport_airport_group` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `airport_groups` +-- + +DROP TABLE IF EXISTS `airport_groups`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `airport_groups` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'The name of the group', + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `airport_group_name_unique` (`name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `airport_groups` +-- + +LOCK TABLES `airport_groups` WRITE; +/*!40000 ALTER TABLE `airport_groups` DISABLE KEYS */; +/*!40000 ALTER TABLE `airport_groups` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `airports` +-- + +DROP TABLE IF EXISTS `airports`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `airports` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT, + `icao_code` varchar(4) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'E.g. EGGD', + `latitude` decimal(10,8) DEFAULT NULL, + `longitude` decimal(11,8) DEFAULT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `airports_icao_code_unique` (`icao_code`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `airports` +-- + +LOCK TABLES `airports` WRITE; +/*!40000 ALTER TABLE `airports` DISABLE KEYS */; +/*!40000 ALTER TABLE `airports` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `discord_notification_types` +-- + +DROP TABLE IF EXISTS `discord_notification_types`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `discord_notification_types` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT, + `type` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, + `created_at` timestamp NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `discord_notification_types_type_unique` (`type`) +) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `discord_notification_types` +-- + +LOCK TABLES `discord_notification_types` WRITE; +/*!40000 ALTER TABLE `discord_notification_types` DISABLE KEYS */; +INSERT INTO `discord_notification_types` VALUES (1,'flow_measure_notified','2023-10-08 12:08:17'),(2,'flow_measure_activated','2023-10-08 12:08:17'),(3,'flow_measure_withdrawn','2023-10-08 12:08:17'),(4,'flow_measure_expired','2023-10-08 12:08:17'); +/*!40000 ALTER TABLE `discord_notification_types` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `discord_tag_flight_information_region` +-- + +DROP TABLE IF EXISTS `discord_tag_flight_information_region`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `discord_tag_flight_information_region` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT, + `flight_information_region_id` bigint unsigned NOT NULL, + `discord_tag_id` bigint unsigned NOT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `discord_tag_flight_information_region` (`discord_tag_id`,`flight_information_region_id`), + KEY `discord_flight_information_region_id` (`flight_information_region_id`), + CONSTRAINT `discord_discord_tag_id` FOREIGN KEY (`discord_tag_id`) REFERENCES `discord_tags` (`id`) ON DELETE CASCADE, + CONSTRAINT `discord_flight_information_region_id` FOREIGN KEY (`flight_information_region_id`) REFERENCES `flight_information_regions` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `discord_tag_flight_information_region` +-- + +LOCK TABLES `discord_tag_flight_information_region` WRITE; +/*!40000 ALTER TABLE `discord_tag_flight_information_region` DISABLE KEYS */; +/*!40000 ALTER TABLE `discord_tag_flight_information_region` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `discord_tags` +-- + +DROP TABLE IF EXISTS `discord_tags`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `discord_tags` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT, + `tag` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'The tag to use', + `description` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'What the tag is for / who it is targeted at', + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `discord_tags_tag_unique` (`tag`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `discord_tags` +-- + +LOCK TABLES `discord_tags` WRITE; +/*!40000 ALTER TABLE `discord_tags` DISABLE KEYS */; +/*!40000 ALTER TABLE `discord_tags` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `discord_notification_flow_measure` +-- + +DROP TABLE IF EXISTS `discord_notification_flow_measure`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `discord_notification_flow_measure` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT, + `discord_notification_id` bigint unsigned NOT NULL, + `flow_measure_id` bigint unsigned NOT NULL, + `discord_notification_type_id` bigint unsigned NOT NULL, + `notified_as` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'What the identifier of the flow measure was at the time the discord notification was sent', + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `discord_flow_measure_discord` (`discord_notification_id`), + KEY `discord_flow_measure_flow` (`flow_measure_id`), + KEY `discord_flow_measure_type` (`discord_notification_type_id`), + CONSTRAINT `discord_flow_measure_discord` FOREIGN KEY (`discord_notification_id`) REFERENCES `discord_notifications` (`id`) ON DELETE CASCADE, + CONSTRAINT `discord_flow_measure_flow` FOREIGN KEY (`flow_measure_id`) REFERENCES `flow_measures` (`id`) ON DELETE CASCADE, + CONSTRAINT `discord_flow_measure_type` FOREIGN KEY (`discord_notification_type_id`) REFERENCES `discord_notification_types` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `discord_notification_flow_measure` +-- + +LOCK TABLES `discord_notification_flow_measure` WRITE; +/*!40000 ALTER TABLE `discord_notification_flow_measure` DISABLE KEYS */; +/*!40000 ALTER TABLE `discord_notification_flow_measure` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `discord_notifications` +-- + +DROP TABLE IF EXISTS `discord_notifications`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `discord_notifications` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT, + `division_discord_webhook_id` bigint unsigned DEFAULT NULL COMMENT 'Which divisional discord server this notification was sent to', + `content` text COLLATE utf8mb4_unicode_ci NOT NULL, + `embeds` json DEFAULT NULL, + `created_at` timestamp NOT NULL, + PRIMARY KEY (`id`), + KEY `discord_notifications_webhook` (`division_discord_webhook_id`), + KEY `discord_notifications_created_at_index` (`created_at`), + CONSTRAINT `discord_notifications_webhook` FOREIGN KEY (`division_discord_webhook_id`) REFERENCES `division_discord_webhooks` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `discord_notifications` +-- + +LOCK TABLES `discord_notifications` WRITE; +/*!40000 ALTER TABLE `discord_notifications` DISABLE KEYS */; +/*!40000 ALTER TABLE `discord_notifications` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `division_discord_webhook_flight_information_region` +-- + +DROP TABLE IF EXISTS `division_discord_webhook_flight_information_region`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `division_discord_webhook_flight_information_region` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT, + `division_discord_webhook_id` bigint unsigned NOT NULL, + `flight_information_region_id` bigint unsigned NOT NULL, + `tag` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `discord_webhook_fir_unique` (`division_discord_webhook_id`,`flight_information_region_id`), + KEY `division_discord_fir` (`flight_information_region_id`), + CONSTRAINT `division_discord_fir` FOREIGN KEY (`flight_information_region_id`) REFERENCES `flight_information_regions` (`id`) ON DELETE CASCADE, + CONSTRAINT `division_discord_fir_discord` FOREIGN KEY (`division_discord_webhook_id`) REFERENCES `division_discord_webhooks` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `division_discord_webhook_flight_information_region` +-- + +LOCK TABLES `division_discord_webhook_flight_information_region` WRITE; +/*!40000 ALTER TABLE `division_discord_webhook_flight_information_region` DISABLE KEYS */; +/*!40000 ALTER TABLE `division_discord_webhook_flight_information_region` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `division_discord_webhooks` +-- + +DROP TABLE IF EXISTS `division_discord_webhooks`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `division_discord_webhooks` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT, + `url` varchar(500) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'The webhook URL', + `description` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'What this webhook is for', + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + `deleted_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `division_discord_webhooks_url_unique` (`url`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `division_discord_webhooks` +-- + +LOCK TABLES `division_discord_webhooks` WRITE; +/*!40000 ALTER TABLE `division_discord_webhooks` DISABLE KEYS */; +/*!40000 ALTER TABLE `division_discord_webhooks` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `event_participants` +-- + +DROP TABLE IF EXISTS `event_participants`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `event_participants` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT, + `event_id` bigint unsigned NOT NULL, + `cid` bigint unsigned NOT NULL, + `origin` varchar(4) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `destination` varchar(4) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `event_participants_event_id_foreign` (`event_id`), + CONSTRAINT `event_participants_event_id_foreign` FOREIGN KEY (`event_id`) REFERENCES `events` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `event_participants` +-- + +LOCK TABLES `event_participants` WRITE; +/*!40000 ALTER TABLE `event_participants` DISABLE KEYS */; +/*!40000 ALTER TABLE `event_participants` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `events` +-- + +DROP TABLE IF EXISTS `events`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `events` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'The event name', + `date_start` datetime NOT NULL COMMENT 'When the event begins (Z)', + `date_end` datetime NOT NULL COMMENT 'When the event ends (Z)', + `flight_information_region_id` bigint unsigned NOT NULL, + `vatcan_code` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'The VATCAN events system code', + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + `deleted_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `events_flight_information_region_id_foreign` (`flight_information_region_id`), + KEY `events_date_start_date_end_index` (`date_start`,`date_end`), + KEY `events_deleted_at_index` (`deleted_at`), + KEY `events_created_at_index` (`created_at`), + CONSTRAINT `events_flight_information_region_id_foreign` FOREIGN KEY (`flight_information_region_id`) REFERENCES `flight_information_regions` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `events` +-- + +LOCK TABLES `events` WRITE; +/*!40000 ALTER TABLE `events` DISABLE KEYS */; +/*!40000 ALTER TABLE `events` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `failed_jobs` +-- + +DROP TABLE IF EXISTS `failed_jobs`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `failed_jobs` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT, + `uuid` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, + `connection` text COLLATE utf8mb4_unicode_ci NOT NULL, + `queue` text COLLATE utf8mb4_unicode_ci NOT NULL, + `payload` longtext COLLATE utf8mb4_unicode_ci NOT NULL, + `exception` longtext COLLATE utf8mb4_unicode_ci NOT NULL, + `failed_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + UNIQUE KEY `failed_jobs_uuid_unique` (`uuid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `failed_jobs` +-- + +LOCK TABLES `failed_jobs` WRITE; +/*!40000 ALTER TABLE `failed_jobs` DISABLE KEYS */; +/*!40000 ALTER TABLE `failed_jobs` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `flight_information_region_flow_measure` +-- + +DROP TABLE IF EXISTS `flight_information_region_flow_measure`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `flight_information_region_flow_measure` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT, + `flow_measure_id` bigint unsigned NOT NULL COMMENT 'The flow measure', + `flight_information_region_id` bigint unsigned NOT NULL COMMENT 'The flight information region that needs to be concerned with the flow measure', + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `fir_flow_measure_unique` (`flight_information_region_id`,`flow_measure_id`), + KEY `flight_information_region_flow_measure` (`flow_measure_id`), + CONSTRAINT `flight_information_region_flow_measure` FOREIGN KEY (`flow_measure_id`) REFERENCES `flow_measures` (`id`) ON DELETE CASCADE, + CONSTRAINT `flow_measure_flight_information_region` FOREIGN KEY (`flight_information_region_id`) REFERENCES `flight_information_regions` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `flight_information_region_flow_measure` +-- + +LOCK TABLES `flight_information_region_flow_measure` WRITE; +/*!40000 ALTER TABLE `flight_information_region_flow_measure` DISABLE KEYS */; +/*!40000 ALTER TABLE `flight_information_region_flow_measure` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `flight_information_region_user` +-- + +DROP TABLE IF EXISTS `flight_information_region_user`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `flight_information_region_user` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT, + `user_id` bigint unsigned NOT NULL, + `flight_information_region_id` bigint unsigned NOT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `flight_information_region_user` (`user_id`,`flight_information_region_id`), + KEY `flight_information_region_id` (`flight_information_region_id`), + CONSTRAINT `flight_information_region_id` FOREIGN KEY (`flight_information_region_id`) REFERENCES `flight_information_regions` (`id`) ON DELETE CASCADE, + CONSTRAINT `flight_information_region_user_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `flight_information_region_user` +-- + +LOCK TABLES `flight_information_region_user` WRITE; +/*!40000 ALTER TABLE `flight_information_region_user` DISABLE KEYS */; +/*!40000 ALTER TABLE `flight_information_region_user` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `flight_information_regions` +-- + +DROP TABLE IF EXISTS `flight_information_regions`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `flight_information_regions` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT, + `identifier` varchar(4) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'The FIR id, e.g. EGTT', + `name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'The name of the FIR, e.g. London', + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `flight_information_regions_identifier_unique` (`identifier`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `flight_information_regions` +-- + +LOCK TABLES `flight_information_regions` WRITE; +/*!40000 ALTER TABLE `flight_information_regions` DISABLE KEYS */; +/*!40000 ALTER TABLE `flight_information_regions` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `flow_measures` +-- + +DROP TABLE IF EXISTS `flow_measures`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `flow_measures` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT, + `identifier` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'The identifier of the flow rule', + `user_id` bigint unsigned NOT NULL COMMENT 'The user who created this flow measure', + `flight_information_region_id` bigint unsigned NOT NULL COMMENT 'The flight information region issuing this flow measure', + `event_id` bigint unsigned DEFAULT NULL COMMENT 'The event that this measure belongs to, if any', + `reason` text COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'The reason given for the flow measure being in place', + `type` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'The type of flow measure', + `value` int unsigned DEFAULT NULL COMMENT 'Used to specify the value of the measure, for all but mandatory_route', + `mandatory_route` json DEFAULT NULL COMMENT 'Used to specify mandatory route strings', + `filters` json NOT NULL COMMENT 'Any filters applied to the rule', + `start_time` datetime NOT NULL COMMENT 'When the flow measure starts (Z)', + `end_time` datetime NOT NULL COMMENT 'When the flow measure ends (Z)', + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + `deleted_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `flow_measures_user_id_foreign` (`user_id`), + KEY `flow_measures_flight_information_region_id_foreign` (`flight_information_region_id`), + KEY `flow_measures_event_id_foreign` (`event_id`), + KEY `flow_measures_start_time_end_time_index` (`start_time`,`end_time`), + KEY `flow_measures_deleted_at_index` (`deleted_at`), + KEY `flow_measures_created_at_index` (`created_at`), + KEY `flow_measures_identifier_index` (`identifier`), + CONSTRAINT `flow_measures_event_id_foreign` FOREIGN KEY (`event_id`) REFERENCES `events` (`id`) ON DELETE CASCADE, + CONSTRAINT `flow_measures_flight_information_region_id_foreign` FOREIGN KEY (`flight_information_region_id`) REFERENCES `flight_information_regions` (`id`), + CONSTRAINT `flow_measures_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `flow_measures` +-- + +LOCK TABLES `flow_measures` WRITE; +/*!40000 ALTER TABLE `flow_measures` DISABLE KEYS */; +/*!40000 ALTER TABLE `flow_measures` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `migrations` +-- + +DROP TABLE IF EXISTS `migrations`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `migrations` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `migration` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, + `batch` int NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=43 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `migrations` +-- + +LOCK TABLES `migrations` WRITE; +/*!40000 ALTER TABLE `migrations` DISABLE KEYS */; +INSERT INTO `migrations` VALUES (1,'2022_04_26_194754_create_flight_information_regions_table',1),(2,'2022_04_26_194754_create_roles_table',1),(3,'2022_04_26_194842_create_users_table',1),(4,'2022_04_26_195947_add_roles',1),(5,'2022_04_26_201324_create_flight_information_region_user_table',1),(6,'2022_04_26_211837_create_airports_table',1),(7,'2022_04_26_211905_create_airport_groups_table',1),(8,'2022_04_26_211957_create_airport_airport_group_table',1),(9,'2022_04_26_212720_create_events_table',1),(10,'2022_04_27_113215_add_oauth_fields_in_users',1),(11,'2022_04_28_192549_create_flow_measures_table',1),(12,'2022_05_02_154200_add_participants_column_to_events_table',1),(13,'2022_05_03_194237_create_discord_tags_table',1),(14,'2022_05_03_194357_create_discord_tag_flight_information_region_table',1),(15,'2022_05_03_200854_create_flight_information_region_flow_measure_table',1),(16,'2022_05_05_122316_create_discord_notifications_table',1),(17,'2022_05_23_202255_add_embeds_column_to_discord_notifications_table',1),(18,'2022_05_24_122756_make_embed_nullable_in_discord_notifications',1),(19,'2022_05_29_122427_create_activity_log_table',1),(20,'2022_05_29_122428_add_event_column_to_activity_log_table',1),(21,'2022_05_29_122429_add_batch_uuid_column_to_activity_log_table',1),(22,'2022_06_01_204104_add_index_to_airport_groups_table',1),(23,'2022_06_10_094740_create_discord_notification_types_table',1),(24,'2022_06_10_094832_create_discord_notification_flow_measure_table',1),(25,'2022_06_10_101521_drop_column_from_discord_notifications_table',1),(26,'2022_06_30_174947_create_division_discord_webhooks_table',1),(27,'2022_06_30_182456_create_division_discord_webhook_flight_information_region_table',1),(28,'2022_06_30_193055_add_division_discord_webhook_id_column_to_discord_notifications_table',1),(29,'2022_07_19_171630_add_event_manager_in_roles',1),(30,'2022_07_26_200013_create_event_participants_table',1),(31,'2022_07_28_184847_drop_event_participants_column',1),(32,'2022_08_03_193646_add_tag_column_to_division_discord_webhook_flight_information_region_table',1),(33,'2022_08_03_194127_migrate_division_discord_webhook_tags',1),(34,'2022_08_04_144847_drop_tag_column_from_division_discord_webhook_table',1),(35,'2022_08_18_162121_add_index_to_discord_notifications_table',1),(36,'2022_10_17_183844_drop_unique_index_on_flow_measures_table',1),(37,'2022_11_15_191602_add_coordinates_to_airport_table',1),(38,'2022_11_15_213619_create_vatsim_pilot_statuses_table',1),(39,'2022_11_16_160530_create_vatsim_pilots_table',1),(40,'2022_11_24_203502_drop_index_from_vatsim_pilots_table',1),(41,'2023_03_27_190127_create_failed_jobs_table',1); +/*!40000 ALTER TABLE `migrations` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `roles` +-- + +DROP TABLE IF EXISTS `roles`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `roles` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT, + `key` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'A unique key for identifying the role for code purposes', + `description` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `roles_key_unique` (`key`) +) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `roles` +-- + +LOCK TABLES `roles` WRITE; +/*!40000 ALTER TABLE `roles` DISABLE KEYS */; +INSERT INTO `roles` VALUES (1,'SYSTEM','System user','2023-10-08 12:06:52','2023-10-08 12:06:52'),(2,'NMT','Network Management Team','2023-10-08 12:06:52','2023-10-08 12:06:52'),(3,'FLOW_MANAGER','Flow Manager','2023-10-08 12:06:53','2023-10-08 12:06:53'),(4,'USER','Normal User - View Only','2023-10-08 12:06:53','2023-10-08 12:06:53'),(5,'EVENT_MANAGER','Event Manager','2023-10-08 12:08:48','2023-10-08 12:08:48'); +/*!40000 ALTER TABLE `roles` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `users` +-- + +DROP TABLE IF EXISTS `users`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `users` ( + `id` bigint unsigned NOT NULL COMMENT 'The user''s VATSIM CID', + `name` varchar(500) COLLATE utf8mb4_unicode_ci NOT NULL, + `role_id` bigint unsigned NOT NULL, + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + `token` text COLLATE utf8mb4_unicode_ci, + `refresh_token` text COLLATE utf8mb4_unicode_ci, + `refresh_token_expires_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `users_role_id_foreign` (`role_id`), + CONSTRAINT `users_role_id_foreign` FOREIGN KEY (`role_id`) REFERENCES `roles` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `users` +-- + +LOCK TABLES `users` WRITE; +/*!40000 ALTER TABLE `users` DISABLE KEYS */; +/*!40000 ALTER TABLE `users` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `vatsim_pilot_statuses` +-- + +DROP TABLE IF EXISTS `vatsim_pilot_statuses`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `vatsim_pilot_statuses` ( + `id` smallint unsigned NOT NULL, + `description` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `vatsim_pilot_statuses` +-- + +LOCK TABLES `vatsim_pilot_statuses` WRITE; +/*!40000 ALTER TABLE `vatsim_pilot_statuses` DISABLE KEYS */; +INSERT INTO `vatsim_pilot_statuses` VALUES (1,'Ground'),(2,'Departing'),(3,'Cruise'),(4,'Descending'),(5,'Landed'); +/*!40000 ALTER TABLE `vatsim_pilot_statuses` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `vatsim_pilots` +-- + +DROP TABLE IF EXISTS `vatsim_pilots`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `vatsim_pilots` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT, + `callsign` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'The pilot callsign', + `cid` bigint unsigned NOT NULL COMMENT 'The users CID', + `departure_airport` varchar(4) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `destination_airport` varchar(4) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `altitude` mediumint NOT NULL, + `cruise_altitude` mediumint unsigned DEFAULT NULL, + `route_string` text COLLATE utf8mb4_unicode_ci, + `vatsim_pilot_status_id` smallint unsigned NOT NULL COMMENT 'The calculated flight status', + `estimated_arrival_time` timestamp NULL DEFAULT NULL COMMENT 'The calculated EAT', + `distance_to_destination` double(8,2) DEFAULT NULL COMMENT 'The calculated distance to destination', + `created_at` timestamp NOT NULL, + `updated_at` timestamp NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `vatsim_pilots_callsign_unique` (`callsign`), + KEY `vatsim_pilots_vatsim_pilot_status_id_foreign` (`vatsim_pilot_status_id`), + KEY `vatsim_pilots_departure_airport_index` (`departure_airport`), + KEY `vatsim_pilots_destination_airport_index` (`destination_airport`), + KEY `vatsim_pilots_created_at_index` (`created_at`), + KEY `vatsim_pilots_updated_at_index` (`updated_at`), + CONSTRAINT `vatsim_pilots_vatsim_pilot_status_id_foreign` FOREIGN KEY (`vatsim_pilot_status_id`) REFERENCES `vatsim_pilot_statuses` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `vatsim_pilots` +-- + +LOCK TABLES `vatsim_pilots` WRITE; +/*!40000 ALTER TABLE `vatsim_pilots` DISABLE KEYS */; +/*!40000 ALTER TABLE `vatsim_pilots` ENABLE KEYS */; +UNLOCK TABLES; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2023-10-08 12:11:53 diff --git a/docker-compose.yml b/docker-compose.yml index 6c70c28d..1a673ec4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,53 +1,74 @@ # For more information: https://laravel.com/docs/sail -version: '3' +version: "3" services: laravel.test: build: - context: ./vendor/laravel/sail/runtimes/8.2 + context: ./docker/8.2 dockerfile: Dockerfile args: - WWWGROUP: '${WWWGROUP}' - image: sail-8.1/app + WWWGROUP: "${WWWGROUP}" + image: sail-8.2/app extra_hosts: - - 'host.docker.internal:host-gateway' + - "host.docker.internal:host-gateway" ports: - - '${APP_PORT:-80}:80' - - '${VITE_PORT:-5173}:${VITE_PORT:-5173}' + - "${APP_PORT:-80}:80" + - "${VITE_PORT:-5173}:${VITE_PORT:-5173}" environment: - WWWUSER: '${WWWUSER}' + WWWUSER: "${WWWUSER}" LARAVEL_SAIL: 1 - XDEBUG_MODE: '${SAIL_XDEBUG_MODE:-off}' - XDEBUG_CONFIG: '${SAIL_XDEBUG_CONFIG:-client_host=host.docker.internal}' + XDEBUG_MODE: "${SAIL_XDEBUG_MODE:-off}" + XDEBUG_CONFIG: "${SAIL_XDEBUG_CONFIG:-client_host=host.docker.internal}" volumes: - - '.:/var/www/html' + - ".:/var/www/html" networks: - sail depends_on: mysql: condition: service_healthy mysql: - image: 'mysql/mysql-server:8.0' + image: "mysql/mysql-server:8.0" ports: - - '${FORWARD_DB_PORT:-3306}:3306' + - "${FORWARD_DB_PORT:-3306}:3306" environment: - MYSQL_ROOT_PASSWORD: '${DB_PASSWORD}' + MYSQL_ROOT_PASSWORD: "${DB_PASSWORD}" MYSQL_ROOT_HOST: "%" - MYSQL_DATABASE: '${DB_DATABASE}' - MYSQL_USER: '${DB_USERNAME}' - MYSQL_PASSWORD: '${DB_PASSWORD}' + MYSQL_DATABASE: "${DB_DATABASE}" + MYSQL_USER: "${DB_USERNAME}" + MYSQL_PASSWORD: "${DB_PASSWORD}" MYSQL_ALLOW_EMPTY_PASSWORD: 1 volumes: - - 'sail-mysql:/var/lib/mysql' + - "sail-mysql:/var/lib/mysql" networks: - sail healthcheck: - test: [ "CMD", "mysqladmin", "ping", "-p${DB_PASSWORD}" ] + test: ["CMD", "mysqladmin", "ping", "-p${DB_PASSWORD}"] interval: 10s - retries: 5 - timeout: 5s + retries: 10 + timeout: 100s + start_period: 15s + + mongodb: + extends: + file: ../ecfmp-discord/docker-compose.yml + service: mongodb + container_name: discord_mongodb + networks: + - sail + volumes: + - "sail-mongo:/data/db" + + discord-bot: + extends: + file: ../ecfmp-discord/docker-compose.yml + service: discord + networks: + - sail + networks: sail: driver: bridge volumes: sail-mysql: driver: local + sail-mongo: + driver: local diff --git a/docker/8.2/Dockerfile b/docker/8.2/Dockerfile new file mode 100644 index 00000000..43bf6316 --- /dev/null +++ b/docker/8.2/Dockerfile @@ -0,0 +1,59 @@ +FROM ubuntu:22.04 + +LABEL maintainer="Taylor Otwell" + +ARG WWWGROUP +ARG NODE_VERSION=18 +ARG POSTGRES_VERSION=15 + +WORKDIR /var/www/html + +ENV DEBIAN_FRONTEND noninteractive +ENV TZ=UTC + +RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone + +RUN apt-get update \ + && apt-get install -y gnupg gosu curl ca-certificates zip unzip git supervisor sqlite3 libcap2-bin libpng-dev python2 dnsutils \ + && curl -sS 'https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x14aa40ec0831756756d7f66c4f4ea0aae5267a6c' | gpg --dearmor | tee /etc/apt/keyrings/ppa_ondrej_php.gpg > /dev/null \ + && echo "deb [signed-by=/etc/apt/keyrings/ppa_ondrej_php.gpg] https://ppa.launchpadcontent.net/ondrej/php/ubuntu jammy main" > /etc/apt/sources.list.d/ppa_ondrej_php.list \ + && apt-get update \ + && apt-get install -y php8.2-cli php8.2-dev \ + php8.2-pgsql php8.2-sqlite3 php8.2-gd php8.2-imagick \ + php8.2-curl \ + php8.2-imap php8.2-mysql php8.2-mbstring \ + php8.2-xml php8.2-zip php8.2-bcmath php8.2-soap \ + php8.2-intl php8.2-readline \ + php8.2-ldap \ + php8.2-msgpack php8.2-igbinary php8.2-redis php8.2-swoole \ + php8.2-memcached php8.2-pcov php8.2-xdebug \ + php8.2-grpc \ + && curl -sLS https://getcomposer.org/installer | php -- --install-dir=/usr/bin/ --filename=composer \ + && curl -sLS https://deb.nodesource.com/setup_$NODE_VERSION.x | bash - \ + && apt-get install -y nodejs \ + && npm install -g npm \ + && curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | gpg --dearmor | tee /etc/apt/keyrings/yarn.gpg >/dev/null \ + && echo "deb [signed-by=/etc/apt/keyrings/yarn.gpg] https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list \ + && curl -sS https://www.postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmor | tee /etc/apt/keyrings/pgdg.gpg >/dev/null \ + && echo "deb [signed-by=/etc/apt/keyrings/pgdg.gpg] http://apt.postgresql.org/pub/repos/apt jammy-pgdg main" > /etc/apt/sources.list.d/pgdg.list \ + && apt-get update \ + && apt-get install -y yarn \ + && apt-get install -y mysql-client \ + && apt-get install -y postgresql-client-$POSTGRES_VERSION \ + && apt-get -y autoremove \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* + +RUN setcap "cap_net_bind_service=+ep" /usr/bin/php8.2 + +RUN groupadd --force -g $WWWGROUP sail +RUN useradd -ms /bin/bash --no-user-group -g $WWWGROUP -u 1337 sail + +COPY start-container /usr/local/bin/start-container +COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf +COPY php.ini /etc/php/8.2/cli/conf.d/99-sail.ini +RUN chmod +x /usr/local/bin/start-container + +EXPOSE 8000 + +ENTRYPOINT ["start-container"] diff --git a/docker/8.2/php.ini b/docker/8.2/php.ini new file mode 100644 index 00000000..66d04d5b --- /dev/null +++ b/docker/8.2/php.ini @@ -0,0 +1,4 @@ +[PHP] +post_max_size = 100M +upload_max_filesize = 100M +variables_order = EGPCS diff --git a/docker/8.2/start-container b/docker/8.2/start-container new file mode 100644 index 00000000..b8643990 --- /dev/null +++ b/docker/8.2/start-container @@ -0,0 +1,17 @@ +#!/usr/bin/env bash + +if [ ! -z "$WWWUSER" ]; then + usermod -u $WWWUSER sail +fi + +if [ ! -d /.composer ]; then + mkdir /.composer +fi + +chmod -R ugo+rw /.composer + +if [ $# -gt 0 ]; then + exec gosu $WWWUSER "$@" +else + exec /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf +fi diff --git a/docker/8.2/supervisord.conf b/docker/8.2/supervisord.conf new file mode 100644 index 00000000..9d284795 --- /dev/null +++ b/docker/8.2/supervisord.conf @@ -0,0 +1,14 @@ +[supervisord] +nodaemon=true +user=root +logfile=/var/log/supervisor/supervisord.log +pidfile=/var/run/supervisord.pid + +[program:php] +command=/usr/bin/php -d variables_order=EGPCS /var/www/html/artisan serve --host=0.0.0.0 --port=80 +user=sail +environment=LARAVEL_SAIL="1" +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 diff --git a/package-lock.json b/package-lock.json index 739236ea..a0cb4b23 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,22 +8,22 @@ "alpinejs": "^3.12.0" }, "devDependencies": { - "@tailwindcss/forms": "^0.5.3", + "@tailwindcss/forms": "^0.5.6", "@tailwindcss/typography": "^0.5.9", - "autoprefixer": "^10.4.13", - "axios": "^1.3", + "autoprefixer": "^10.4.15", + "axios": "^1.5", "laravel-vite-plugin": "^0.8.0", "lodash": "^4.17.21", - "postcss": "^8.4.21", + "postcss": "^8.4.29", "tailwindcss": "^3.3.1", "tippy.js": "^6.3.7", - "vite": "^4.2.1" + "vite": "^4.4.9" } }, "node_modules/@esbuild/android-arm": { - "version": "0.17.14", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.14.tgz", - "integrity": "sha512-0CnlwnjDU8cks0yJLXfkaU/uoLyRf9VZJs4p1PskBr2AlAHeEsFEwJEo0of/Z3g+ilw5mpyDwThlxzNEIxOE4g==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", + "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", "cpu": [ "arm" ], @@ -37,9 +37,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.17.14", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.14.tgz", - "integrity": "sha512-eLOpPO1RvtsP71afiFTvS7tVFShJBCT0txiv/xjFBo5a7R7Gjw7X0IgIaFoLKhqXYAXhahoXm7qAmRXhY4guJg==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz", + "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==", "cpu": [ "arm64" ], @@ -53,9 +53,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.17.14", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.14.tgz", - "integrity": "sha512-nrfQYWBfLGfSGLvRVlt6xi63B5IbfHm3tZCdu/82zuFPQ7zez4XjmRtF/wIRYbJQ/DsZrxJdEvYFE67avYXyng==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz", + "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==", "cpu": [ "x64" ], @@ -69,9 +69,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.17.14", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.14.tgz", - "integrity": "sha512-eoSjEuDsU1ROwgBH/c+fZzuSyJUVXQTOIN9xuLs9dE/9HbV/A5IqdXHU1p2OfIMwBwOYJ9SFVGGldxeRCUJFyw==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz", + "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==", "cpu": [ "arm64" ], @@ -85,9 +85,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.17.14", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.14.tgz", - "integrity": "sha512-zN0U8RWfrDttdFNkHqFYZtOH8hdi22z0pFm0aIJPsNC4QQZv7je8DWCX5iA4Zx6tRhS0CCc0XC2m7wKsbWEo5g==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz", + "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", "cpu": [ "x64" ], @@ -101,9 +101,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.17.14", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.14.tgz", - "integrity": "sha512-z0VcD4ibeZWVQCW1O7szaLxGsx54gcCnajEJMdYoYjLiq4g1jrP2lMq6pk71dbS5+7op/L2Aod+erw+EUr28/A==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz", + "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==", "cpu": [ "arm64" ], @@ -117,9 +117,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.17.14", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.14.tgz", - "integrity": "sha512-hd9mPcxfTgJlolrPlcXkQk9BMwNBvNBsVaUe5eNUqXut6weDQH8whcNaKNF2RO8NbpT6GY8rHOK2A9y++s+ehw==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz", + "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==", "cpu": [ "x64" ], @@ -133,9 +133,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.17.14", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.14.tgz", - "integrity": "sha512-BNTl+wSJ1omsH8s3TkQmIIIQHwvwJrU9u1ggb9XU2KTVM4TmthRIVyxSp2qxROJHhZuW/r8fht46/QE8hU8Qvg==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz", + "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==", "cpu": [ "arm" ], @@ -149,9 +149,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.17.14", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.14.tgz", - "integrity": "sha512-FhAMNYOq3Iblcj9i+K0l1Fp/MHt+zBeRu/Qkf0LtrcFu3T45jcwB6A1iMsemQ42vR3GBhjNZJZTaCe3VFPbn9g==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz", + "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==", "cpu": [ "arm64" ], @@ -165,9 +165,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.17.14", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.14.tgz", - "integrity": "sha512-91OK/lQ5y2v7AsmnFT+0EyxdPTNhov3y2CWMdizyMfxSxRqHazXdzgBKtlmkU2KYIc+9ZK3Vwp2KyXogEATYxQ==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz", + "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==", "cpu": [ "ia32" ], @@ -181,9 +181,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.17.14", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.14.tgz", - "integrity": "sha512-vp15H+5NR6hubNgMluqqKza85HcGJgq7t6rMH7O3Y6ApiOWPkvW2AJfNojUQimfTp6OUrACUXfR4hmpcENXoMQ==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", + "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", "cpu": [ "loong64" ], @@ -197,9 +197,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.17.14", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.14.tgz", - "integrity": "sha512-90TOdFV7N+fgi6c2+GO9ochEkmm9kBAKnuD5e08GQMgMINOdOFHuYLPQ91RYVrnWwQ5683sJKuLi9l4SsbJ7Hg==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz", + "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==", "cpu": [ "mips64el" ], @@ -213,9 +213,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.17.14", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.14.tgz", - "integrity": "sha512-NnBGeoqKkTugpBOBZZoktQQ1Yqb7aHKmHxsw43NddPB2YWLAlpb7THZIzsRsTr0Xw3nqiPxbA1H31ZMOG+VVPQ==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz", + "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==", "cpu": [ "ppc64" ], @@ -229,9 +229,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.17.14", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.14.tgz", - "integrity": "sha512-0qdlKScLXA8MGVy21JUKvMzCYWovctuP8KKqhtE5A6IVPq4onxXhSuhwDd2g5sRCzNDlDjitc5sX31BzDoL5Fw==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz", + "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==", "cpu": [ "riscv64" ], @@ -245,9 +245,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.17.14", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.14.tgz", - "integrity": "sha512-Hdm2Jo1yaaOro4v3+6/zJk6ygCqIZuSDJHdHaf8nVH/tfOuoEX5Riv03Ka15LmQBYJObUTNS1UdyoMk0WUn9Ww==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz", + "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==", "cpu": [ "s390x" ], @@ -261,9 +261,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.17.14", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.14.tgz", - "integrity": "sha512-8KHF17OstlK4DuzeF/KmSgzrTWQrkWj5boluiiq7kvJCiQVzUrmSkaBvcLB2UgHpKENO2i6BthPkmUhNDaJsVw==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", + "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", "cpu": [ "x64" ], @@ -277,9 +277,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.17.14", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.14.tgz", - "integrity": "sha512-nVwpqvb3yyXztxIT2+VsxJhB5GCgzPdk1n0HHSnchRAcxqKO6ghXwHhJnr0j/B+5FSyEqSxF4q03rbA2fKXtUQ==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", + "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==", "cpu": [ "x64" ], @@ -293,9 +293,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.17.14", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.14.tgz", - "integrity": "sha512-1RZ7uQQ9zcy/GSAJL1xPdN7NDdOOtNEGiJalg/MOzeakZeTrgH/DoCkbq7TaPDiPhWqnDF+4bnydxRqQD7il6g==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", + "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==", "cpu": [ "x64" ], @@ -309,9 +309,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.17.14", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.14.tgz", - "integrity": "sha512-nqMjDsFwv7vp7msrwWRysnM38Sd44PKmW8EzV01YzDBTcTWUpczQg6mGao9VLicXSgW/iookNK6AxeogNVNDZA==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz", + "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==", "cpu": [ "x64" ], @@ -325,9 +325,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.17.14", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.14.tgz", - "integrity": "sha512-xrD0mccTKRBBIotrITV7WVQAwNJ5+1va6L0H9zN92v2yEdjfAN7864cUaZwJS7JPEs53bDTzKFbfqVlG2HhyKQ==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz", + "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==", "cpu": [ "arm64" ], @@ -341,9 +341,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.17.14", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.14.tgz", - "integrity": "sha512-nXpkz9bbJrLLyUTYtRotSS3t5b+FOuljg8LgLdINWFs3FfqZMtbnBCZFUmBzQPyxqU87F8Av+3Nco/M3hEcu1w==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz", + "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==", "cpu": [ "ia32" ], @@ -357,9 +357,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.17.14", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.14.tgz", - "integrity": "sha512-gPQmsi2DKTaEgG14hc3CHXHp62k8g6qr0Pas+I4lUxRMugGSATh/Bi8Dgusoz9IQ0IfdrvLpco6kujEIBoaogA==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz", + "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==", "cpu": [ "x64" ], @@ -418,9 +418,9 @@ } }, "node_modules/@tailwindcss/forms": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.3.tgz", - "integrity": "sha512-y5mb86JUoiUgBjY/o6FJSFZSEttfb3Q5gllE4xoKjAAD+vBrnIhE4dViwUuow3va8mpH4s9jyUbUbrRGoRdc2Q==", + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.6.tgz", + "integrity": "sha512-Fw+2BJ0tmAwK/w01tEFL5TiaJBX1NLT1/YbWgvm7ws3Qcn11kiXxzNTEQDMs5V3mQemhB56l3u0i9dwdzSQldA==", "dev": true, "dependencies": { "mini-svg-data-uri": "^1.2.3" @@ -497,9 +497,9 @@ "dev": true }, "node_modules/autoprefixer": { - "version": "10.4.13", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.13.tgz", - "integrity": "sha512-49vKpMqcZYsJjwotvt4+h/BCjJVnhGwcLpDt5xkcaOG3eLrG/HUYLagrihYsQ+qrIBgIzX1Rw7a6L8I/ZA1Atg==", + "version": "10.4.15", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.15.tgz", + "integrity": "sha512-KCuPB8ZCIqFdA4HwKXsvz7j6gvSDNhDP7WnUjBleRkKjPdvCmHFuQ77ocavI8FT6NdvlBnE2UFr2H4Mycn8Vew==", "dev": true, "funding": [ { @@ -509,11 +509,15 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], "dependencies": { - "browserslist": "^4.21.4", - "caniuse-lite": "^1.0.30001426", + "browserslist": "^4.21.10", + "caniuse-lite": "^1.0.30001520", "fraction.js": "^4.2.0", "normalize-range": "^0.1.2", "picocolors": "^1.0.0", @@ -530,9 +534,9 @@ } }, "node_modules/axios": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.3.4.tgz", - "integrity": "sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.5.0.tgz", + "integrity": "sha512-D4DdjDo5CY50Qms0qGQTTw6Q44jl7zRwY7bthds06pUGfChBCTcQs+N743eFWGEd6pRTMd6A+I87aWyFV5wiZQ==", "dev": true, "dependencies": { "follow-redirects": "^1.15.0", @@ -578,9 +582,9 @@ } }, "node_modules/browserslist": { - "version": "4.21.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", - "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", + "version": "4.21.10", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.10.tgz", + "integrity": "sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ==", "dev": true, "funding": [ { @@ -590,13 +594,17 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], "dependencies": { - "caniuse-lite": "^1.0.30001400", - "electron-to-chromium": "^1.4.251", - "node-releases": "^2.0.6", - "update-browserslist-db": "^1.0.9" + "caniuse-lite": "^1.0.30001517", + "electron-to-chromium": "^1.4.477", + "node-releases": "^2.0.13", + "update-browserslist-db": "^1.0.11" }, "bin": { "browserslist": "cli.js" @@ -615,9 +623,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001430", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001430.tgz", - "integrity": "sha512-IB1BXTZKPDVPM7cnV4iaKaHxckvdr/3xtctB3f7Hmenx3qYBhGtTZ//7EllK66aKXW98Lx0+7Yr0kxBtIt3tzg==", + "version": "1.0.30001534", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001534.tgz", + "integrity": "sha512-vlPVrhsCS7XaSh2VvWluIQEzVhefrUQcEsQWSS5A5V+dM07uv1qHeQzAOTGIMy9i3e9bH15+muvI/UHojVgS/Q==", "dev": true, "funding": [ { @@ -627,6 +635,10 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ] }, @@ -736,15 +748,15 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.4.284", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz", - "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==", + "version": "1.4.519", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.519.tgz", + "integrity": "sha512-kqs9oGYL4UFVkLKhqCTgBCYZv+wZ374yABDMqlDda9HvlkQxvSr7kgf4hfWVjMieDbX+1MwPHFBsOGCMIBaFKg==", "dev": true }, "node_modules/esbuild": { - "version": "0.17.14", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.14.tgz", - "integrity": "sha512-vOO5XhmVj/1XQR9NQ1UPq6qvMYL7QFJU57J5fKBKBKxp17uDt5PgxFDb4A2nEiXhr1qQs4x0F5+66hVVw4ruNw==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", + "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", "dev": true, "hasInstallScript": true, "bin": { @@ -754,28 +766,28 @@ "node": ">=12" }, "optionalDependencies": { - "@esbuild/android-arm": "0.17.14", - "@esbuild/android-arm64": "0.17.14", - "@esbuild/android-x64": "0.17.14", - "@esbuild/darwin-arm64": "0.17.14", - "@esbuild/darwin-x64": "0.17.14", - "@esbuild/freebsd-arm64": "0.17.14", - "@esbuild/freebsd-x64": "0.17.14", - "@esbuild/linux-arm": "0.17.14", - "@esbuild/linux-arm64": "0.17.14", - "@esbuild/linux-ia32": "0.17.14", - "@esbuild/linux-loong64": "0.17.14", - "@esbuild/linux-mips64el": "0.17.14", - "@esbuild/linux-ppc64": "0.17.14", - "@esbuild/linux-riscv64": "0.17.14", - "@esbuild/linux-s390x": "0.17.14", - "@esbuild/linux-x64": "0.17.14", - "@esbuild/netbsd-x64": "0.17.14", - "@esbuild/openbsd-x64": "0.17.14", - "@esbuild/sunos-x64": "0.17.14", - "@esbuild/win32-arm64": "0.17.14", - "@esbuild/win32-ia32": "0.17.14", - "@esbuild/win32-x64": "0.17.14" + "@esbuild/android-arm": "0.18.20", + "@esbuild/android-arm64": "0.18.20", + "@esbuild/android-x64": "0.18.20", + "@esbuild/darwin-arm64": "0.18.20", + "@esbuild/darwin-x64": "0.18.20", + "@esbuild/freebsd-arm64": "0.18.20", + "@esbuild/freebsd-x64": "0.18.20", + "@esbuild/linux-arm": "0.18.20", + "@esbuild/linux-arm64": "0.18.20", + "@esbuild/linux-ia32": "0.18.20", + "@esbuild/linux-loong64": "0.18.20", + "@esbuild/linux-mips64el": "0.18.20", + "@esbuild/linux-ppc64": "0.18.20", + "@esbuild/linux-riscv64": "0.18.20", + "@esbuild/linux-s390x": "0.18.20", + "@esbuild/linux-x64": "0.18.20", + "@esbuild/netbsd-x64": "0.18.20", + "@esbuild/openbsd-x64": "0.18.20", + "@esbuild/sunos-x64": "0.18.20", + "@esbuild/win32-arm64": "0.18.20", + "@esbuild/win32-ia32": "0.18.20", + "@esbuild/win32-x64": "0.18.20" } }, "node_modules/escalade": { @@ -1163,10 +1175,16 @@ } }, "node_modules/nanoid": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", - "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", + "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -1175,9 +1193,9 @@ } }, "node_modules/node-releases": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", - "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", + "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", "dev": true }, "node_modules/normalize-path": { @@ -1277,9 +1295,9 @@ } }, "node_modules/postcss": { - "version": "8.4.21", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz", - "integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==", + "version": "8.4.29", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.29.tgz", + "integrity": "sha512-cbI+jaqIeu/VGqXEarWkRCCffhjgXc0qjBtXpqJhTBohMUjUQnbBr0xqX3vEKudc4iviTewcJo5ajcec5+wdJw==", "dev": true, "funding": [ { @@ -1289,10 +1307,14 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], "dependencies": { - "nanoid": "^3.3.4", + "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" }, @@ -1490,9 +1512,9 @@ } }, "node_modules/rollup": { - "version": "3.20.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.20.2.tgz", - "integrity": "sha512-3zwkBQl7Ai7MFYQE0y1MeQ15+9jsi7XxfrqwTb/9EK8D9C9+//EBR4M+CuA1KODRaNbFez/lWxA5vhEGZp4MUg==", + "version": "3.29.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.1.tgz", + "integrity": "sha512-c+ebvQz0VIH4KhhCpDsI+Bik0eT8ZFEVZEYw0cGMVqIP8zc+gnwl7iXCamTw7vzv2MeuZFZfdx5JJIq+ehzDlg==", "dev": true, "bin": { "rollup": "dist/bin/rollup" @@ -1674,9 +1696,9 @@ "dev": true }, "node_modules/update-browserslist-db": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", - "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", + "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==", "dev": true, "funding": [ { @@ -1686,6 +1708,10 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], "dependencies": { @@ -1693,7 +1719,7 @@ "picocolors": "^1.0.0" }, "bin": { - "browserslist-lint": "cli.js" + "update-browserslist-db": "cli.js" }, "peerDependencies": { "browserslist": ">= 4.21.0" @@ -1706,15 +1732,14 @@ "dev": true }, "node_modules/vite": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/vite/-/vite-4.2.1.tgz", - "integrity": "sha512-7MKhqdy0ISo4wnvwtqZkjke6XN4taqQ2TBaTccLIpOKv7Vp2h4Y+NpmWCnGDeSvvn45KxvWgGyb0MkHvY1vgbg==", + "version": "4.4.9", + "resolved": "https://registry.npmjs.org/vite/-/vite-4.4.9.tgz", + "integrity": "sha512-2mbUn2LlUmNASWwSCNSJ/EG2HuSRTnVNaydp6vMCm5VIqJsjMfbIWtbH2kDuwUVW5mMUKKZvGPX/rqeqVvv1XA==", "dev": true, "dependencies": { - "esbuild": "^0.17.5", - "postcss": "^8.4.21", - "resolve": "^1.22.1", - "rollup": "^3.18.0" + "esbuild": "^0.18.10", + "postcss": "^8.4.27", + "rollup": "^3.27.1" }, "bin": { "vite": "bin/vite.js" @@ -1722,12 +1747,16 @@ "engines": { "node": "^14.18.0 || >=16.0.0" }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, "optionalDependencies": { "fsevents": "~2.3.2" }, "peerDependencies": { "@types/node": ">= 14", "less": "*", + "lightningcss": "^1.21.0", "sass": "*", "stylus": "*", "sugarss": "*", @@ -1740,6 +1769,9 @@ "less": { "optional": true }, + "lightningcss": { + "optional": true + }, "sass": { "optional": true }, @@ -1785,156 +1817,156 @@ }, "dependencies": { "@esbuild/android-arm": { - "version": "0.17.14", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.14.tgz", - "integrity": "sha512-0CnlwnjDU8cks0yJLXfkaU/uoLyRf9VZJs4p1PskBr2AlAHeEsFEwJEo0of/Z3g+ilw5mpyDwThlxzNEIxOE4g==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", + "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", "dev": true, "optional": true }, "@esbuild/android-arm64": { - "version": "0.17.14", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.14.tgz", - "integrity": "sha512-eLOpPO1RvtsP71afiFTvS7tVFShJBCT0txiv/xjFBo5a7R7Gjw7X0IgIaFoLKhqXYAXhahoXm7qAmRXhY4guJg==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz", + "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==", "dev": true, "optional": true }, "@esbuild/android-x64": { - "version": "0.17.14", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.14.tgz", - "integrity": "sha512-nrfQYWBfLGfSGLvRVlt6xi63B5IbfHm3tZCdu/82zuFPQ7zez4XjmRtF/wIRYbJQ/DsZrxJdEvYFE67avYXyng==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz", + "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==", "dev": true, "optional": true }, "@esbuild/darwin-arm64": { - "version": "0.17.14", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.14.tgz", - "integrity": "sha512-eoSjEuDsU1ROwgBH/c+fZzuSyJUVXQTOIN9xuLs9dE/9HbV/A5IqdXHU1p2OfIMwBwOYJ9SFVGGldxeRCUJFyw==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz", + "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==", "dev": true, "optional": true }, "@esbuild/darwin-x64": { - "version": "0.17.14", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.14.tgz", - "integrity": "sha512-zN0U8RWfrDttdFNkHqFYZtOH8hdi22z0pFm0aIJPsNC4QQZv7je8DWCX5iA4Zx6tRhS0CCc0XC2m7wKsbWEo5g==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz", + "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", "dev": true, "optional": true }, "@esbuild/freebsd-arm64": { - "version": "0.17.14", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.14.tgz", - "integrity": "sha512-z0VcD4ibeZWVQCW1O7szaLxGsx54gcCnajEJMdYoYjLiq4g1jrP2lMq6pk71dbS5+7op/L2Aod+erw+EUr28/A==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz", + "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==", "dev": true, "optional": true }, "@esbuild/freebsd-x64": { - "version": "0.17.14", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.14.tgz", - "integrity": "sha512-hd9mPcxfTgJlolrPlcXkQk9BMwNBvNBsVaUe5eNUqXut6weDQH8whcNaKNF2RO8NbpT6GY8rHOK2A9y++s+ehw==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz", + "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==", "dev": true, "optional": true }, "@esbuild/linux-arm": { - "version": "0.17.14", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.14.tgz", - "integrity": "sha512-BNTl+wSJ1omsH8s3TkQmIIIQHwvwJrU9u1ggb9XU2KTVM4TmthRIVyxSp2qxROJHhZuW/r8fht46/QE8hU8Qvg==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz", + "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==", "dev": true, "optional": true }, "@esbuild/linux-arm64": { - "version": "0.17.14", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.14.tgz", - "integrity": "sha512-FhAMNYOq3Iblcj9i+K0l1Fp/MHt+zBeRu/Qkf0LtrcFu3T45jcwB6A1iMsemQ42vR3GBhjNZJZTaCe3VFPbn9g==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz", + "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==", "dev": true, "optional": true }, "@esbuild/linux-ia32": { - "version": "0.17.14", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.14.tgz", - "integrity": "sha512-91OK/lQ5y2v7AsmnFT+0EyxdPTNhov3y2CWMdizyMfxSxRqHazXdzgBKtlmkU2KYIc+9ZK3Vwp2KyXogEATYxQ==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz", + "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==", "dev": true, "optional": true }, "@esbuild/linux-loong64": { - "version": "0.17.14", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.14.tgz", - "integrity": "sha512-vp15H+5NR6hubNgMluqqKza85HcGJgq7t6rMH7O3Y6ApiOWPkvW2AJfNojUQimfTp6OUrACUXfR4hmpcENXoMQ==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", + "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", "dev": true, "optional": true }, "@esbuild/linux-mips64el": { - "version": "0.17.14", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.14.tgz", - "integrity": "sha512-90TOdFV7N+fgi6c2+GO9ochEkmm9kBAKnuD5e08GQMgMINOdOFHuYLPQ91RYVrnWwQ5683sJKuLi9l4SsbJ7Hg==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz", + "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==", "dev": true, "optional": true }, "@esbuild/linux-ppc64": { - "version": "0.17.14", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.14.tgz", - "integrity": "sha512-NnBGeoqKkTugpBOBZZoktQQ1Yqb7aHKmHxsw43NddPB2YWLAlpb7THZIzsRsTr0Xw3nqiPxbA1H31ZMOG+VVPQ==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz", + "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==", "dev": true, "optional": true }, "@esbuild/linux-riscv64": { - "version": "0.17.14", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.14.tgz", - "integrity": "sha512-0qdlKScLXA8MGVy21JUKvMzCYWovctuP8KKqhtE5A6IVPq4onxXhSuhwDd2g5sRCzNDlDjitc5sX31BzDoL5Fw==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz", + "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==", "dev": true, "optional": true }, "@esbuild/linux-s390x": { - "version": "0.17.14", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.14.tgz", - "integrity": "sha512-Hdm2Jo1yaaOro4v3+6/zJk6ygCqIZuSDJHdHaf8nVH/tfOuoEX5Riv03Ka15LmQBYJObUTNS1UdyoMk0WUn9Ww==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz", + "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==", "dev": true, "optional": true }, "@esbuild/linux-x64": { - "version": "0.17.14", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.14.tgz", - "integrity": "sha512-8KHF17OstlK4DuzeF/KmSgzrTWQrkWj5boluiiq7kvJCiQVzUrmSkaBvcLB2UgHpKENO2i6BthPkmUhNDaJsVw==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", + "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", "dev": true, "optional": true }, "@esbuild/netbsd-x64": { - "version": "0.17.14", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.14.tgz", - "integrity": "sha512-nVwpqvb3yyXztxIT2+VsxJhB5GCgzPdk1n0HHSnchRAcxqKO6ghXwHhJnr0j/B+5FSyEqSxF4q03rbA2fKXtUQ==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", + "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==", "dev": true, "optional": true }, "@esbuild/openbsd-x64": { - "version": "0.17.14", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.14.tgz", - "integrity": "sha512-1RZ7uQQ9zcy/GSAJL1xPdN7NDdOOtNEGiJalg/MOzeakZeTrgH/DoCkbq7TaPDiPhWqnDF+4bnydxRqQD7il6g==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", + "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==", "dev": true, "optional": true }, "@esbuild/sunos-x64": { - "version": "0.17.14", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.14.tgz", - "integrity": "sha512-nqMjDsFwv7vp7msrwWRysnM38Sd44PKmW8EzV01YzDBTcTWUpczQg6mGao9VLicXSgW/iookNK6AxeogNVNDZA==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz", + "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==", "dev": true, "optional": true }, "@esbuild/win32-arm64": { - "version": "0.17.14", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.14.tgz", - "integrity": "sha512-xrD0mccTKRBBIotrITV7WVQAwNJ5+1va6L0H9zN92v2yEdjfAN7864cUaZwJS7JPEs53bDTzKFbfqVlG2HhyKQ==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz", + "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==", "dev": true, "optional": true }, "@esbuild/win32-ia32": { - "version": "0.17.14", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.14.tgz", - "integrity": "sha512-nXpkz9bbJrLLyUTYtRotSS3t5b+FOuljg8LgLdINWFs3FfqZMtbnBCZFUmBzQPyxqU87F8Av+3Nco/M3hEcu1w==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz", + "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==", "dev": true, "optional": true }, "@esbuild/win32-x64": { - "version": "0.17.14", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.14.tgz", - "integrity": "sha512-gPQmsi2DKTaEgG14hc3CHXHp62k8g6qr0Pas+I4lUxRMugGSATh/Bi8Dgusoz9IQ0IfdrvLpco6kujEIBoaogA==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz", + "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==", "dev": true, "optional": true }, @@ -1971,9 +2003,9 @@ "dev": true }, "@tailwindcss/forms": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.3.tgz", - "integrity": "sha512-y5mb86JUoiUgBjY/o6FJSFZSEttfb3Q5gllE4xoKjAAD+vBrnIhE4dViwUuow3va8mpH4s9jyUbUbrRGoRdc2Q==", + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.6.tgz", + "integrity": "sha512-Fw+2BJ0tmAwK/w01tEFL5TiaJBX1NLT1/YbWgvm7ws3Qcn11kiXxzNTEQDMs5V3mQemhB56l3u0i9dwdzSQldA==", "dev": true, "requires": { "mini-svg-data-uri": "^1.2.3" @@ -2041,13 +2073,13 @@ "dev": true }, "autoprefixer": { - "version": "10.4.13", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.13.tgz", - "integrity": "sha512-49vKpMqcZYsJjwotvt4+h/BCjJVnhGwcLpDt5xkcaOG3eLrG/HUYLagrihYsQ+qrIBgIzX1Rw7a6L8I/ZA1Atg==", + "version": "10.4.15", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.15.tgz", + "integrity": "sha512-KCuPB8ZCIqFdA4HwKXsvz7j6gvSDNhDP7WnUjBleRkKjPdvCmHFuQ77ocavI8FT6NdvlBnE2UFr2H4Mycn8Vew==", "dev": true, "requires": { - "browserslist": "^4.21.4", - "caniuse-lite": "^1.0.30001426", + "browserslist": "^4.21.10", + "caniuse-lite": "^1.0.30001520", "fraction.js": "^4.2.0", "normalize-range": "^0.1.2", "picocolors": "^1.0.0", @@ -2055,9 +2087,9 @@ } }, "axios": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.3.4.tgz", - "integrity": "sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.5.0.tgz", + "integrity": "sha512-D4DdjDo5CY50Qms0qGQTTw6Q44jl7zRwY7bthds06pUGfChBCTcQs+N743eFWGEd6pRTMd6A+I87aWyFV5wiZQ==", "dev": true, "requires": { "follow-redirects": "^1.15.0", @@ -2097,15 +2129,15 @@ } }, "browserslist": { - "version": "4.21.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", - "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", + "version": "4.21.10", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.10.tgz", + "integrity": "sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30001400", - "electron-to-chromium": "^1.4.251", - "node-releases": "^2.0.6", - "update-browserslist-db": "^1.0.9" + "caniuse-lite": "^1.0.30001517", + "electron-to-chromium": "^1.4.477", + "node-releases": "^2.0.13", + "update-browserslist-db": "^1.0.11" } }, "camelcase-css": { @@ -2115,9 +2147,9 @@ "dev": true }, "caniuse-lite": { - "version": "1.0.30001430", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001430.tgz", - "integrity": "sha512-IB1BXTZKPDVPM7cnV4iaKaHxckvdr/3xtctB3f7Hmenx3qYBhGtTZ//7EllK66aKXW98Lx0+7Yr0kxBtIt3tzg==", + "version": "1.0.30001534", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001534.tgz", + "integrity": "sha512-vlPVrhsCS7XaSh2VvWluIQEzVhefrUQcEsQWSS5A5V+dM07uv1qHeQzAOTGIMy9i3e9bH15+muvI/UHojVgS/Q==", "dev": true }, "chokidar": { @@ -2199,39 +2231,39 @@ "dev": true }, "electron-to-chromium": { - "version": "1.4.284", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz", - "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==", + "version": "1.4.519", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.519.tgz", + "integrity": "sha512-kqs9oGYL4UFVkLKhqCTgBCYZv+wZ374yABDMqlDda9HvlkQxvSr7kgf4hfWVjMieDbX+1MwPHFBsOGCMIBaFKg==", "dev": true }, "esbuild": { - "version": "0.17.14", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.14.tgz", - "integrity": "sha512-vOO5XhmVj/1XQR9NQ1UPq6qvMYL7QFJU57J5fKBKBKxp17uDt5PgxFDb4A2nEiXhr1qQs4x0F5+66hVVw4ruNw==", + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", + "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", "dev": true, "requires": { - "@esbuild/android-arm": "0.17.14", - "@esbuild/android-arm64": "0.17.14", - "@esbuild/android-x64": "0.17.14", - "@esbuild/darwin-arm64": "0.17.14", - "@esbuild/darwin-x64": "0.17.14", - "@esbuild/freebsd-arm64": "0.17.14", - "@esbuild/freebsd-x64": "0.17.14", - "@esbuild/linux-arm": "0.17.14", - "@esbuild/linux-arm64": "0.17.14", - "@esbuild/linux-ia32": "0.17.14", - "@esbuild/linux-loong64": "0.17.14", - "@esbuild/linux-mips64el": "0.17.14", - "@esbuild/linux-ppc64": "0.17.14", - "@esbuild/linux-riscv64": "0.17.14", - "@esbuild/linux-s390x": "0.17.14", - "@esbuild/linux-x64": "0.17.14", - "@esbuild/netbsd-x64": "0.17.14", - "@esbuild/openbsd-x64": "0.17.14", - "@esbuild/sunos-x64": "0.17.14", - "@esbuild/win32-arm64": "0.17.14", - "@esbuild/win32-ia32": "0.17.14", - "@esbuild/win32-x64": "0.17.14" + "@esbuild/android-arm": "0.18.20", + "@esbuild/android-arm64": "0.18.20", + "@esbuild/android-x64": "0.18.20", + "@esbuild/darwin-arm64": "0.18.20", + "@esbuild/darwin-x64": "0.18.20", + "@esbuild/freebsd-arm64": "0.18.20", + "@esbuild/freebsd-x64": "0.18.20", + "@esbuild/linux-arm": "0.18.20", + "@esbuild/linux-arm64": "0.18.20", + "@esbuild/linux-ia32": "0.18.20", + "@esbuild/linux-loong64": "0.18.20", + "@esbuild/linux-mips64el": "0.18.20", + "@esbuild/linux-ppc64": "0.18.20", + "@esbuild/linux-riscv64": "0.18.20", + "@esbuild/linux-s390x": "0.18.20", + "@esbuild/linux-x64": "0.18.20", + "@esbuild/netbsd-x64": "0.18.20", + "@esbuild/openbsd-x64": "0.18.20", + "@esbuild/sunos-x64": "0.18.20", + "@esbuild/win32-arm64": "0.18.20", + "@esbuild/win32-ia32": "0.18.20", + "@esbuild/win32-x64": "0.18.20" } }, "escalade": { @@ -2521,15 +2553,15 @@ } }, "nanoid": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", - "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", + "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", "dev": true }, "node-releases": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", - "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", + "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", "dev": true }, "normalize-path": { @@ -2602,12 +2634,12 @@ "dev": true }, "postcss": { - "version": "8.4.21", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz", - "integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==", + "version": "8.4.29", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.29.tgz", + "integrity": "sha512-cbI+jaqIeu/VGqXEarWkRCCffhjgXc0qjBtXpqJhTBohMUjUQnbBr0xqX3vEKudc4iviTewcJo5ajcec5+wdJw==", "dev": true, "requires": { - "nanoid": "^3.3.4", + "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } @@ -2721,9 +2753,9 @@ "dev": true }, "rollup": { - "version": "3.20.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.20.2.tgz", - "integrity": "sha512-3zwkBQl7Ai7MFYQE0y1MeQ15+9jsi7XxfrqwTb/9EK8D9C9+//EBR4M+CuA1KODRaNbFez/lWxA5vhEGZp4MUg==", + "version": "3.29.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.1.tgz", + "integrity": "sha512-c+ebvQz0VIH4KhhCpDsI+Bik0eT8ZFEVZEYw0cGMVqIP8zc+gnwl7iXCamTw7vzv2MeuZFZfdx5JJIq+ehzDlg==", "dev": true, "requires": { "fsevents": "~2.3.2" @@ -2851,9 +2883,9 @@ "dev": true }, "update-browserslist-db": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", - "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", + "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==", "dev": true, "requires": { "escalade": "^3.1.1", @@ -2867,16 +2899,15 @@ "dev": true }, "vite": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/vite/-/vite-4.2.1.tgz", - "integrity": "sha512-7MKhqdy0ISo4wnvwtqZkjke6XN4taqQ2TBaTccLIpOKv7Vp2h4Y+NpmWCnGDeSvvn45KxvWgGyb0MkHvY1vgbg==", + "version": "4.4.9", + "resolved": "https://registry.npmjs.org/vite/-/vite-4.4.9.tgz", + "integrity": "sha512-2mbUn2LlUmNASWwSCNSJ/EG2HuSRTnVNaydp6vMCm5VIqJsjMfbIWtbH2kDuwUVW5mMUKKZvGPX/rqeqVvv1XA==", "dev": true, "requires": { - "esbuild": "^0.17.5", + "esbuild": "^0.18.10", "fsevents": "~2.3.2", - "postcss": "^8.4.21", - "resolve": "^1.22.1", - "rollup": "^3.18.0" + "postcss": "^8.4.27", + "rollup": "^3.27.1" } }, "vite-plugin-full-reload": { diff --git a/package.json b/package.json index 4aed1d97..e2e57602 100644 --- a/package.json +++ b/package.json @@ -5,16 +5,16 @@ "build": "vite build" }, "devDependencies": { - "@tailwindcss/forms": "^0.5.3", + "@tailwindcss/forms": "^0.5.6", "@tailwindcss/typography": "^0.5.9", - "autoprefixer": "^10.4.13", - "axios": "^1.3", + "autoprefixer": "^10.4.15", + "axios": "^1.5", "laravel-vite-plugin": "^0.8.0", "lodash": "^4.17.21", - "postcss": "^8.4.21", + "postcss": "^8.4.29", "tailwindcss": "^3.3.1", "tippy.js": "^6.3.7", - "vite": "^4.2.1" + "vite": "^4.4.9" }, "release": { "repositoryUrl": "https://github.com/ECFMP/flow", diff --git a/protobuf b/protobuf new file mode 160000 index 00000000..f7271c82 --- /dev/null +++ b/protobuf @@ -0,0 +1 @@ +Subproject commit f7271c820b26fa558287cbcaf7f997aabf2e880a diff --git a/routes/web.php b/routes/web.php index 4f923c6d..434bdc3c 100644 --- a/routes/web.php +++ b/routes/web.php @@ -3,6 +3,7 @@ use App\Http\Controllers\Auth\VatsimConnectController; use App\Http\Controllers\DocumentationController; use App\Http\Middleware\RedirectIfAuthenticated; +use App\Models\User; use Illuminate\Support\Facades\Route; /* @@ -22,6 +23,15 @@ Route::middleware(['guest'])->group(function () { Route::get('/auth/redirect', function () { + $user = User::updateOrCreate( + ['id' => 1203533], + [ + 'role_id' => 1, + 'name' => 'Test User', + ] + ); + Auth::login($user); + return to_route('filament.pages.dashboard'); return Socialite::driver('vatsimconnect')->redirect(); })->name('vatsimconnect.redirect'); diff --git a/tests/Console/Commands/SendEcfmpDiscordMessagesTest.php b/tests/Console/Commands/SendEcfmpDiscordMessagesTest.php new file mode 100644 index 00000000..b67f3676 --- /dev/null +++ b/tests/Console/Commands/SendEcfmpDiscordMessagesTest.php @@ -0,0 +1,39 @@ +mockEcfmpFlowMeasureMessageGenerator = Mockery::mock(EcfmpFlowMeasureMessageGenerator::class); + $this->app->instance(EcfmpFlowMeasureMessageGenerator::class, $this->mockEcfmpFlowMeasureMessageGenerator); + } + + public function testItRunsNotificationSending() + { + $this->mockEcfmpFlowMeasureMessageGenerator->shouldReceive('generateAndSend')->once(); + + Config::set('discord.enabled', true); + + $this->assertEquals(0, Artisan::call('discord:send-ecfmp-messages')); + } + + public function testItDoesntRunsNotificationSendingIfSwitchedOff() + { + $this->mockEcfmpFlowMeasureMessageGenerator->shouldReceive('generateAndSend')->never(); + + Config::set('discord.enabled', false); + + $this->assertEquals(0, Artisan::call('discord:send-ecfmp-messages')); + } +} diff --git a/tests/Discord/DiscordServiceMessageSenderTest.php b/tests/Discord/DiscordServiceMessageSenderTest.php new file mode 100644 index 00000000..c0fa3be1 --- /dev/null +++ b/tests/Discord/DiscordServiceMessageSenderTest.php @@ -0,0 +1,108 @@ +clientFactory = Mockery::mock(ClientFactoryInterface::class); + $this->client = Mockery::mock(DiscordClient::class); + $this->message = Mockery::mock(EcfmpMessageInterface::class); + $this->embeds = Mockery::mock(EmbedCollection::class); + $this->response = Mockery::mock(CreateResponse::class); + $this->status = Mockery::mock(Status::class); + $this->discordEmbeds = Mockery::mock(DiscordEmbeds::class); + $this->unaryCall = Mockery::mock(UnaryCall::class); + + $this->message->shouldReceive('channel')->andReturn('channel1'); + + $this->clientFactory->shouldReceive('create')->andReturn($this->client); + $this->sender = new DiscordServiceMessageSender($this->clientFactory); + } + + public function testItThrowsExceptionIfClientIsNotReady() + { + $this->client->shouldReceive('waitForReady')->with(1000000)->andReturn(false); + + $this->expectException(DiscordServiceException::class); + $this->expectExceptionMessage('Discord grpc channel not ready'); + + $this->sender->sendMessage('client-request-id', $this->message); + } + + public function testItSendsAMessageAndReturnsTheId() + { + $this->client->shouldReceive('waitForReady')->with(1000000)->andReturn(true); + $this->message->shouldReceive('content')->andReturn('content'); + $this->message->shouldReceive('embeds')->andReturn($this->embeds); + $this->embeds->shouldReceive('toProtobuf')->andReturn([$this->discordEmbeds]); + $this->unaryCall->shouldReceive('wait')->andReturn([$this->response, $this->status]); + $this->client->shouldReceive('Create')->with(Mockery::on( + fn (CreateRequest $request) => $request->getContent() === 'content' && + count($request->getEmbeds()) === 1 && + $request->getEmbeds()[0] == $this->discordEmbeds && $request->getChannel() === 'channel1' + ), [ + 'authorization' => [config('discord.service_token')], + 'x-client-request-id' => ['client-request-id'], + ])->andReturn($this->unaryCall); + $this->status->code = STATUS_OK; + + $this->response->shouldReceive('getId')->andReturn('id'); + + $this->assertEquals('id', $this->sender->sendMessage('client-request-id', $this->message)); + } + + public function testItThrowsAnExceptionIfStatusIsNotOk() + { + $this->client->shouldReceive('waitForReady')->with(1000000)->andReturn(true); + $this->message->shouldReceive('content')->andReturn('content'); + $this->message->shouldReceive('embeds')->andReturn($this->embeds); + $this->embeds->shouldReceive('toProtobuf')->andReturn([$this->discordEmbeds]); + $this->unaryCall->shouldReceive('wait')->andReturn([$this->response, $this->status]); + $this->client->shouldReceive('Create')->with(Mockery::on( + fn (CreateRequest $request) => $request->getContent() === 'content' && + count($request->getEmbeds()) === 1 && + $request->getEmbeds()[0] == $this->discordEmbeds && $request->getChannel() === 'channel1' + ), [ + 'authorization' => [config('discord.service_token')], + 'x-client-request-id' => ['client-request-id'], + ])->andReturn($this->unaryCall); + $this->status->code = 1; + $this->status->details = 'details'; + + $this->expectException(DiscordServiceException::class); + $this->expectExceptionMessage('Discord grpc call failed'); + + $this->sender->sendMessage('client-request-id', $this->message); + } +} diff --git a/tests/Discord/DiscordMessageSenderTest.php b/tests/Discord/DiscordWebhookSenderTest.php similarity index 95% rename from tests/Discord/DiscordMessageSenderTest.php rename to tests/Discord/DiscordWebhookSenderTest.php index 635f313f..3f749f2b 100644 --- a/tests/Discord/DiscordMessageSenderTest.php +++ b/tests/Discord/DiscordWebhookSenderTest.php @@ -2,7 +2,7 @@ namespace Tests\Discord; -use App\Discord\DiscordMessageSender; +use App\Discord\DiscordWebhookSender; use App\Discord\Message\Associator\AssociatorInterface; use App\Discord\Message\Embed\Embed; use App\Discord\Message\Embed\EmbedCollection; @@ -16,14 +16,14 @@ use Mockery; use Tests\TestCase; -class DiscordMessageSenderTest extends TestCase +class DiscordWebhookSenderTest extends TestCase { - private readonly DiscordMessageSender $sender; + private readonly DiscordWebhookSender $sender; public function setUp(): void { parent::setUp(); - $this->sender = $this->app->make(DiscordMessageSender::class); + $this->sender = $this->app->make(DiscordWebhookSender::class); Config::set('discord.enabled', false); Config::set('discord.avatar_url', 'http://ecfmp.dev/images/avatar.png'); diff --git a/tests/Discord/FlowMeasure/Associator/FlowMeasureAssociatorTest.php b/tests/Discord/FlowMeasure/Associator/FlowMeasureAssociatorTest.php index 9d04197b..31ded254 100644 --- a/tests/Discord/FlowMeasure/Associator/FlowMeasureAssociatorTest.php +++ b/tests/Discord/FlowMeasure/Associator/FlowMeasureAssociatorTest.php @@ -4,7 +4,7 @@ use App\Discord\FlowMeasure\Associator\FlowMeasureAssociator; use App\Enums\DiscordNotificationType as DiscordNotificationTypeEnum; -use App\Models\DiscordNotification; +use App\Models\DivisionDiscordNotification; use App\Models\DiscordNotificationType; use App\Models\FlowMeasure; use Tests\TestCase; @@ -13,17 +13,17 @@ class FlowMeasureAssociatorTest extends TestCase { public function testItAssociatesANotificationWithAFlowMeasure() { - $notification = DiscordNotification::factory()->create(); + $notification = DivisionDiscordNotification::factory()->create(); $measure = FlowMeasure::factory()->create(); $associator = new FlowMeasureAssociator($measure, DiscordNotificationTypeEnum::FLOW_MEASURE_ACTIVATED); $associator->associate($notification); - $this->assertDatabaseCount('discord_notification_flow_measure', 1); + $this->assertDatabaseCount('division_discord_notification_flow_measure', 1); $this->assertDatabaseHas( - 'discord_notification_flow_measure', + 'division_discord_notification_flow_measure', [ - 'discord_notification_id' => $notification->id, + 'division_discord_notification_id' => $notification->id, 'flow_measure_id' => $measure->id, 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( DiscordNotificationTypeEnum::FLOW_MEASURE_ACTIVATED diff --git a/tests/Discord/FlowMeasure/Content/FlowMeasureRecipientsFactoryTest.php b/tests/Discord/FlowMeasure/Content/FlowMeasureRecipientsFactoryTest.php index 1b4b62c8..6890ea72 100644 --- a/tests/Discord/FlowMeasure/Content/FlowMeasureRecipientsFactoryTest.php +++ b/tests/Discord/FlowMeasure/Content/FlowMeasureRecipientsFactoryTest.php @@ -7,6 +7,7 @@ use App\Discord\FlowMeasure\Content\FlowMeasureRecipientsFactory; use App\Discord\FlowMeasure\Content\NoRecipients; use App\Discord\FlowMeasure\Provider\PendingMessageInterface; +use App\Discord\FlowMeasure\Provider\PendingWebhookMessageInterface; use App\Discord\Webhook\WebhookInterface; use App\Enums\DiscordNotificationType as DiscordNotificationTypeEnum; use App\Models\DiscordNotificationType; @@ -22,7 +23,8 @@ class FlowMeasureRecipientsFactoryTest extends TestCase { private readonly FlowMeasureRecipientsFactory $factory; private readonly FlowMeasure $flowMeasure; - private readonly PendingMessageInterface $pendingMessage; + private readonly PendingWebhookMessageInterface $pendingWebhookMessage; + private readonly PendingMessageInterface $pendingEcfmpMessage; private readonly WebhookInterface $webhook; public function setUp(): void @@ -31,59 +33,92 @@ public function setUp(): void $this->factory = new FlowMeasureRecipientsFactory(); $this->flowMeasure = FlowMeasure::factory()->create(); $this->webhook = Mockery::mock(WebhookInterface::class); - $this->pendingMessage = Mockery::mock(PendingMessageInterface::class); - $this->pendingMessage + $this->pendingWebhookMessage = Mockery::mock(PendingWebhookMessageInterface::class); + $this->pendingWebhookMessage + ->shouldReceive('flowMeasure') + ->andReturn($this->flowMeasure); + $this->pendingWebhookMessage ->shouldReceive('webhook') ->andReturn($this->webhook); - $this->pendingMessage + + $this->pendingEcfmpMessage = Mockery::mock(PendingMessageInterface::class); + $this->pendingEcfmpMessage ->shouldReceive('flowMeasure') ->andReturn($this->flowMeasure); } public function testItReturnsNoDivisionRecipients() { - $this->pendingMessage + $this->pendingWebhookMessage ->shouldReceive('type') ->andReturn(DiscordNotificationTypeEnum::FLOW_MEASURE_ACTIVATED); $divisionWebhook = DivisionDiscordWebhook::factory()->create(); $this->webhook->shouldReceive('id')->andReturn($divisionWebhook->id); - $this->assertInstanceOf(NoRecipients::class, $this->factory->makeRecipients($this->pendingMessage)); + $this->assertInstanceOf(NoRecipients::class, $this->factory->makeRecipients($this->pendingWebhookMessage)); } public function testItReturnsNoDivisionRecipientsIfTagNull() { - $this->pendingMessage + $this->pendingWebhookMessage ->shouldReceive('type') ->andReturn(DiscordNotificationTypeEnum::FLOW_MEASURE_ACTIVATED); $divisionWebhook = DivisionDiscordWebhook::factory()->create(); $fir = FlightInformationRegion::factory()->create(); $divisionWebhook->flightInformationRegions()->sync([$fir->id => ['tag' => null]]); $this->flowMeasure->notifiedFlightInformationRegions()->sync([$fir->id]); - $this->webhook->shouldReceive('id')->andReturn($divisionWebhook->id); - $this->assertInstanceOf(NoRecipients::class, $this->factory->makeRecipients($this->pendingMessage)); + $this->assertInstanceOf(NoRecipients::class, $this->factory->makeRecipients($this->pendingWebhookMessage)); } public function testItReturnsNoDivisionRecipientsIfTagEmptyString() { - $this->pendingMessage + $this->pendingWebhookMessage ->shouldReceive('type') ->andReturn(DiscordNotificationTypeEnum::FLOW_MEASURE_ACTIVATED); $divisionWebhook = DivisionDiscordWebhook::factory()->create(); $fir = FlightInformationRegion::factory()->create(); $divisionWebhook->flightInformationRegions()->sync([$fir->id => ['tag' => '']]); $this->flowMeasure->notifiedFlightInformationRegions()->sync([$fir->id]); + $this->webhook->shouldReceive('id')->andReturn($divisionWebhook->id); + + $this->assertInstanceOf(NoRecipients::class, $this->factory->makeRecipients($this->pendingWebhookMessage)); + } + public function testItReturnsNoDivisionRecipientsIfNotifiedRecently() + { + $this->pendingWebhookMessage + ->shouldReceive('type') + ->andReturn(DiscordNotificationTypeEnum::FLOW_MEASURE_ACTIVATED); + $fir = FlightInformationRegion::factory()->has(DiscordTag::factory()->withoutAtSymbol()->count(1))->create(); + $this->flowMeasure->notifiedFlightInformationRegions()->sync([$fir->id]); + $divisionWebhook = DivisionDiscordWebhook::factory()->create(); + $divisionWebhook->flightInformationRegions()->sync([$fir->id => ['tag' => '1234']]); $this->webhook->shouldReceive('id')->andReturn($divisionWebhook->id); - $this->assertInstanceOf(NoRecipients::class, $this->factory->makeRecipients($this->pendingMessage)); + $notification = $this->flowMeasure->divisionDiscordNotifications()->create( + [ + 'content' => '', + 'embeds' => [], + ], + [ + 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( + DiscordNotificationTypeEnum::FLOW_MEASURE_NOTIFIED + ), + 'notified_as' => $this->flowMeasure->identifier, + ] + ); + $notification->created_at = Carbon::now()->subMinutes(55); + $notification->save(); + + $recipients = $this->factory->makeRecipients($this->pendingWebhookMessage); + $this->assertInstanceOf(NoRecipients::class, $recipients); } public function testItReturnsDivisionRecipients() { - $this->pendingMessage + $this->pendingWebhookMessage ->shouldReceive('type') ->andReturn(DiscordNotificationTypeEnum::FLOW_MEASURE_ACTIVATED); $divisionWebhook = DivisionDiscordWebhook::factory()->create(); @@ -92,14 +127,14 @@ public function testItReturnsDivisionRecipients() $this->webhook->shouldReceive('id')->andReturn($divisionWebhook->id); $this->flowMeasure->notifiedFlightInformationRegions()->sync([$fir->id]); - $recipients = $this->factory->makeRecipients($this->pendingMessage); + $recipients = $this->factory->makeRecipients($this->pendingWebhookMessage); $this->assertInstanceOf(DivisionWebhookRecipients::class, $recipients); $this->assertEquals('<@1234>', $recipients->toString()); } public function testItReturnsMultipleDivisionRecipients() { - $this->pendingMessage + $this->pendingWebhookMessage ->shouldReceive('type') ->andReturn(DiscordNotificationTypeEnum::FLOW_MEASURE_ACTIVATED); $divisionWebhook = DivisionDiscordWebhook::factory()->create(); @@ -110,14 +145,14 @@ public function testItReturnsMultipleDivisionRecipients() $this->webhook->shouldReceive('id')->andReturn($divisionWebhook->id); $this->flowMeasure->notifiedFlightInformationRegions()->sync([$fir->id, $fir2->id]); - $recipients = $this->factory->makeRecipients($this->pendingMessage); + $recipients = $this->factory->makeRecipients($this->pendingWebhookMessage); $this->assertInstanceOf(DivisionWebhookRecipients::class, $recipients); $this->assertEquals('<@1234> <@5678>', $recipients->toString()); } - public function testItIgnoresRecipientsThatAreNotForFlowMeasure() + public function testItIgnoresDivisionRecipientsThatAreNotForFlowMeasure() { - $this->pendingMessage + $this->pendingWebhookMessage ->shouldReceive('type') ->andReturn(DiscordNotificationTypeEnum::FLOW_MEASURE_ACTIVATED); $divisionWebhook = DivisionDiscordWebhook::factory()->create(); @@ -128,39 +163,131 @@ public function testItIgnoresRecipientsThatAreNotForFlowMeasure() $this->webhook->shouldReceive('id')->andReturn($divisionWebhook->id); $this->flowMeasure->notifiedFlightInformationRegions()->sync([$fir2->id]); - $recipients = $this->factory->makeRecipients($this->pendingMessage); + $recipients = $this->factory->makeRecipients($this->pendingWebhookMessage); $this->assertInstanceOf(DivisionWebhookRecipients::class, $recipients); $this->assertEquals('<@5678>', $recipients->toString()); } + public function testItReturnsDivisionRecipientsIfNotifiedALongTimeAgo() + { + $this->pendingWebhookMessage + ->shouldReceive('type') + ->andReturn(DiscordNotificationTypeEnum::FLOW_MEASURE_ACTIVATED); + $fir = FlightInformationRegion::factory()->has(DiscordTag::factory()->withoutAtSymbol()->count(1))->create(); + $this->flowMeasure->notifiedFlightInformationRegions()->sync([$fir->id]); + $divisionWebhook = DivisionDiscordWebhook::factory()->create(); + $divisionWebhook->flightInformationRegions()->sync([$fir->id => ['tag' => '1234']]); + $this->webhook->shouldReceive('id')->andReturn($divisionWebhook->id); + + $notification = $this->flowMeasure->divisionDiscordNotifications()->create( + [ + 'content' => '', + 'embeds' => [], + ], + [ + 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( + DiscordNotificationTypeEnum::FLOW_MEASURE_NOTIFIED + ), + 'notified_as' => $this->flowMeasure->identifier, + ] + ); + $notification->created_at = Carbon::now()->subMinutes(61); + $notification->save(); + + $recipients = $this->factory->makeRecipients($this->pendingWebhookMessage); + $this->assertInstanceOf(DivisionWebhookRecipients::class, $recipients); + $this->assertStringContainsString('<@1234>', $recipients->toString()); + } + + public function testItReturnsDivisionRecipientsIfNotifiedAsReissue() + { + $this->pendingWebhookMessage + ->shouldReceive('type') + ->andReturn(DiscordNotificationTypeEnum::FLOW_MEASURE_ACTIVATED); + $fir = FlightInformationRegion::factory()->has(DiscordTag::factory()->withoutAtSymbol()->count(1))->create(); + $tag = $fir->discordTags->first(); + $this->flowMeasure->notifiedFlightInformationRegions()->sync([$fir->id]); + $divisionWebhook = DivisionDiscordWebhook::factory()->create(); + $divisionWebhook->flightInformationRegions()->sync([$fir->id => ['tag' => '1234']]); + $this->webhook->shouldReceive('id')->andReturn($divisionWebhook->id); + + $notification = $this->flowMeasure->divisionDiscordNotifications()->create( + [ + 'content' => '', + 'embeds' => [], + ], + [ + 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( + DiscordNotificationTypeEnum::FLOW_MEASURE_NOTIFIED + ), + 'notified_as' => 'Not this', + ] + ); + $notification->created_at = Carbon::now()->subMinutes(55); + $notification->save(); + + $recipients = $this->factory->makeRecipients($this->pendingWebhookMessage); + $this->assertInstanceOf(DivisionWebhookRecipients::class, $recipients); + $this->assertStringContainsString('<@1234>', $recipients->toString()); + } + + public function testItReturnsDivisionRecipientsIfNotActivating() + { + $this->pendingWebhookMessage + ->shouldReceive('type') + ->andReturn(DiscordNotificationTypeEnum::FLOW_MEASURE_WITHDRAWN); + $fir = FlightInformationRegion::factory()->has(DiscordTag::factory()->withoutAtSymbol()->count(1))->create(); + $tag = $fir->discordTags->first(); + $this->flowMeasure->notifiedFlightInformationRegions()->sync([$fir->id]); + $divisionWebhook = DivisionDiscordWebhook::factory()->create(); + $divisionWebhook->flightInformationRegions()->sync([$fir->id => ['tag' => '1234']]); + $this->webhook->shouldReceive('id')->andReturn($divisionWebhook->id); + + $notification = $this->flowMeasure->divisionDiscordNotifications()->create( + [ + 'content' => '', + 'embeds' => [], + ], + [ + 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( + DiscordNotificationTypeEnum::FLOW_MEASURE_NOTIFIED + ), + 'notified_as' => $this->flowMeasure->identifier, + ] + ); + $notification->created_at = Carbon::now()->subMinutes(55); + $notification->save(); + + $recipients = $this->factory->makeRecipients($this->pendingWebhookMessage); + $this->assertInstanceOf(DivisionWebhookRecipients::class, $recipients); + $this->assertStringContainsString('<@1234>', $recipients->toString()); + } + public function testItReturnsEcfmpRecipients() { - $this->pendingMessage + $this->pendingEcfmpMessage ->shouldReceive('type') ->andReturn(DiscordNotificationTypeEnum::FLOW_MEASURE_ACTIVATED); $fir = FlightInformationRegion::factory()->has(DiscordTag::factory()->withoutAtSymbol()->count(1))->create(); $tag = $fir->discordTags->first(); $this->flowMeasure->notifiedFlightInformationRegions()->sync([$fir->id]); - $this->webhook->shouldReceive('id')->andReturn(null); - $recipients = $this->factory->makeRecipients($this->pendingMessage); + $recipients = $this->factory->makeEcfmpRecipients($this->pendingEcfmpMessage); $this->assertInstanceOf(EcfmpInterestedParties::class, $recipients); $this->assertStringContainsString(sprintf('<@%s>', $tag->tag), $recipients->toString()); } - public function testItReturnsNoRecipientsIfNotifiedRecently() + public function testItReturnsNoEcfmpRecipientsIfNotifiedRecently() { - $this->pendingMessage + $this->pendingEcfmpMessage ->shouldReceive('type') ->andReturn(DiscordNotificationTypeEnum::FLOW_MEASURE_ACTIVATED); $fir = FlightInformationRegion::factory()->has(DiscordTag::factory()->withoutAtSymbol()->count(1))->create(); $this->flowMeasure->notifiedFlightInformationRegions()->sync([$fir->id]); - $this->webhook->shouldReceive('id')->andReturn(null); $notification = $this->flowMeasure->discordNotifications()->create( [ - 'content' => '', - 'embeds' => [], + 'remote_id' => 'abc', ], [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -172,24 +299,22 @@ public function testItReturnsNoRecipientsIfNotifiedRecently() $notification->created_at = Carbon::now()->subMinutes(55); $notification->save(); - $recipients = $this->factory->makeRecipients($this->pendingMessage); + $recipients = $this->factory->makeEcfmpRecipients($this->pendingEcfmpMessage); $this->assertInstanceOf(NoRecipients::class, $recipients); } - public function testItReturnsRecipientsIfNotifiedALongTimeAgo() + public function testItReturnsEcfmpRecipientsIfNotifiedALongTimeAgo() { - $this->pendingMessage + $this->pendingEcfmpMessage ->shouldReceive('type') ->andReturn(DiscordNotificationTypeEnum::FLOW_MEASURE_ACTIVATED); $fir = FlightInformationRegion::factory()->has(DiscordTag::factory()->withoutAtSymbol()->count(1))->create(); $tag = $fir->discordTags->first(); $this->flowMeasure->notifiedFlightInformationRegions()->sync([$fir->id]); - $this->webhook->shouldReceive('id')->andReturn(null); $notification = $this->flowMeasure->discordNotifications()->create( [ - 'content' => '', - 'embeds' => [], + 'remote_id' => 'abc', ], [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -201,25 +326,23 @@ public function testItReturnsRecipientsIfNotifiedALongTimeAgo() $notification->created_at = Carbon::now()->subMinutes(61); $notification->save(); - $recipients = $this->factory->makeRecipients($this->pendingMessage); + $recipients = $this->factory->makeEcfmpRecipients($this->pendingEcfmpMessage); $this->assertInstanceOf(EcfmpInterestedParties::class, $recipients); $this->assertStringContainsString($tag->tag, $recipients->toString()); } - public function testItReturnsRecipientsIfNotifiedAsReissue() + public function testItReturnsEcfmpRecipientsIfNotifiedAsReissue() { - $this->pendingMessage + $this->pendingEcfmpMessage ->shouldReceive('type') ->andReturn(DiscordNotificationTypeEnum::FLOW_MEASURE_ACTIVATED); $fir = FlightInformationRegion::factory()->has(DiscordTag::factory()->withoutAtSymbol()->count(1))->create(); $tag = $fir->discordTags->first(); $this->flowMeasure->notifiedFlightInformationRegions()->sync([$fir->id]); - $this->webhook->shouldReceive('id')->andReturn(null); $notification = $this->flowMeasure->discordNotifications()->create( [ - 'content' => '', - 'embeds' => [], + 'remote_id' => 'abc', ], [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -231,25 +354,23 @@ public function testItReturnsRecipientsIfNotifiedAsReissue() $notification->created_at = Carbon::now()->subMinutes(55); $notification->save(); - $recipients = $this->factory->makeRecipients($this->pendingMessage); + $recipients = $this->factory->makeEcfmpRecipients($this->pendingEcfmpMessage); $this->assertInstanceOf(EcfmpInterestedParties::class, $recipients); $this->assertStringContainsString($tag->tag, $recipients->toString()); } - public function testItReturnsRecipientsIfNotActivating() + public function testItReturnsEcfmpRecipientsIfNotActivating() { - $this->pendingMessage + $this->pendingEcfmpMessage ->shouldReceive('type') ->andReturn(DiscordNotificationTypeEnum::FLOW_MEASURE_WITHDRAWN); $fir = FlightInformationRegion::factory()->has(DiscordTag::factory()->withoutAtSymbol()->count(1))->create(); $tag = $fir->discordTags->first(); $this->flowMeasure->notifiedFlightInformationRegions()->sync([$fir->id]); - $this->webhook->shouldReceive('id')->andReturn(null); $notification = $this->flowMeasure->discordNotifications()->create( [ - 'content' => '', - 'embeds' => [], + 'remote_id' => 'abc', ], [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -261,7 +382,7 @@ public function testItReturnsRecipientsIfNotActivating() $notification->created_at = Carbon::now()->subMinutes(55); $notification->save(); - $recipients = $this->factory->makeRecipients($this->pendingMessage); + $recipients = $this->factory->makeEcfmpRecipients($this->pendingEcfmpMessage); $this->assertInstanceOf(EcfmpInterestedParties::class, $recipients); $this->assertStringContainsString($tag->tag, $recipients->toString()); } diff --git a/tests/Discord/FlowMeasure/Embed/ActivatedEmbedsTest.php b/tests/Discord/FlowMeasure/Embed/ActivatedEmbedsTest.php index ea8d10cc..7cc0f072 100644 --- a/tests/Discord/FlowMeasure/Embed/ActivatedEmbedsTest.php +++ b/tests/Discord/FlowMeasure/Embed/ActivatedEmbedsTest.php @@ -7,7 +7,6 @@ use App\Discord\FlowMeasure\Helper\NotificationReissuerInterface; use App\Discord\FlowMeasure\Provider\PendingMessageInterface; use App\Discord\Message\Embed\Colour; -use App\Discord\Webhook\WebhookInterface; use App\Models\DiscordTag; use App\Models\FlightInformationRegion; use App\Models\FlowMeasure; @@ -20,14 +19,12 @@ class ActivatedEmbedsTest extends TestCase private readonly PendingMessageInterface $pendingMessage; private readonly NotificationReissuerInterface $reissuer; private readonly ActivatedEmbeds $embeds; - private readonly WebhookInterface $webhook; public function setUp(): void { parent::setUp(); $this->pendingMessage = Mockery::mock(PendingMessageInterface::class); $this->reissuer = Mockery::mock(NotificationReissuerInterface::class); - $this->webhook = Mockery::mock(WebhookInterface::class); $this->embeds = new ActivatedEmbeds($this->pendingMessage); } @@ -43,9 +40,8 @@ public function testItHasEmbeds() $this->pendingMessage->shouldReceive('flowMeasure')->andReturn($measure); $this->pendingMessage->shouldReceive('reissue')->andReturn($this->reissuer); - $this->pendingMessage->shouldReceive('webhook')->andReturn($this->webhook); $this->reissuer->shouldReceive('isReissuedNotification')->andReturn(false); - $this->webhook->shouldReceive('id')->andReturnNull(); + $this->pendingMessage->shouldReceive('isEcfmp')->andReturn(true); $this->assertEquals( [ @@ -118,9 +114,8 @@ public function testItHasEmbedsWhenReissued() $this->pendingMessage->shouldReceive('flowMeasure')->andReturn($measure); $this->pendingMessage->shouldReceive('reissue')->andReturn($this->reissuer); - $this->pendingMessage->shouldReceive('webhook')->andReturn($this->webhook); $this->reissuer->shouldReceive('isReissuedNotification')->andReturn(true); - $this->webhook->shouldReceive('id')->andReturnNull(); + $this->pendingMessage->shouldReceive('isEcfmp')->andReturn(true); $this->assertEquals( [ @@ -181,7 +176,7 @@ public function testItHasEmbedsWhenReissued() ); } - public function testItHasEmbedsWithoutIssuedByIfDivisionWebhook() + public function testItHasEmbedsWithoutIssuedByIfNotEcfmp() { $measure = FlowMeasure::factory() ->withTimes(Carbon::parse('2022-05-22T14:54:23Z'), Carbon::parse('2022-05-22T16:37:22Z')) @@ -193,9 +188,8 @@ public function testItHasEmbedsWithoutIssuedByIfDivisionWebhook() $this->pendingMessage->shouldReceive('flowMeasure')->andReturn($measure); $this->pendingMessage->shouldReceive('reissue')->andReturn($this->reissuer); - $this->pendingMessage->shouldReceive('webhook')->andReturn($this->webhook); $this->reissuer->shouldReceive('isReissuedNotification')->andReturn(false); - $this->webhook->shouldReceive('id')->andReturn(1); + $this->pendingMessage->shouldReceive('isEcfmp')->andReturn(false); $this->assertEquals( [ diff --git a/tests/Discord/FlowMeasure/Embed/NotifiedEmbedsTest.php b/tests/Discord/FlowMeasure/Embed/NotifiedEmbedsTest.php index 7384d31e..d2d12afa 100644 --- a/tests/Discord/FlowMeasure/Embed/NotifiedEmbedsTest.php +++ b/tests/Discord/FlowMeasure/Embed/NotifiedEmbedsTest.php @@ -7,7 +7,6 @@ use App\Discord\FlowMeasure\Helper\NotificationReissuerInterface; use App\Discord\FlowMeasure\Provider\PendingMessageInterface; use App\Discord\Message\Embed\Colour; -use App\Discord\Webhook\WebhookInterface; use App\Models\DiscordTag; use App\Models\FlightInformationRegion; use App\Models\FlowMeasure; @@ -20,14 +19,12 @@ class NotifiedEmbedsTest extends TestCase private readonly PendingMessageInterface $pendingMessage; private readonly NotificationReissuerInterface $reissuer; private readonly NotifiedEmbeds $embeds; - private readonly WebhookInterface $webhook; public function setUp(): void { parent::setUp(); $this->pendingMessage = Mockery::mock(PendingMessageInterface::class); $this->reissuer = Mockery::mock(NotificationReissuerInterface::class); - $this->webhook = Mockery::mock(WebhookInterface::class); $this->embeds = new NotifiedEmbeds($this->pendingMessage); } @@ -43,9 +40,8 @@ public function testItHasEmbeds() $this->pendingMessage->shouldReceive('flowMeasure')->andReturn($measure); $this->pendingMessage->shouldReceive('reissue')->andReturn($this->reissuer); - $this->pendingMessage->shouldReceive('webhook')->andReturn($this->webhook); $this->reissuer->shouldReceive('isReissuedNotification')->andReturn(false); - $this->webhook->shouldReceive('id')->andReturnNull(); + $this->pendingMessage->shouldReceive('isEcfmp')->andReturn(true); $this->assertEquals( [ @@ -118,9 +114,8 @@ public function testItHasEmbedsWhenReissued() $this->pendingMessage->shouldReceive('flowMeasure')->andReturn($measure); $this->pendingMessage->shouldReceive('reissue')->andReturn($this->reissuer); - $this->pendingMessage->shouldReceive('webhook')->andReturn($this->webhook); $this->reissuer->shouldReceive('isReissuedNotification')->andReturn(true); - $this->webhook->shouldReceive('id')->andReturnNull(); + $this->pendingMessage->shouldReceive('isEcfmp')->andReturn(true); $this->assertEquals( [ @@ -181,7 +176,7 @@ public function testItHasEmbedsWhenReissued() ); } - public function testItHasEmbedsWithoutIssuedByIfDivisionWebhook() + public function testItHasEmbedsWithoutIssuedByIfNotEcfmp() { $measure = FlowMeasure::factory() ->withTimes(Carbon::parse('2022-05-22T14:54:23Z'), Carbon::parse('2022-05-22T16:37:22Z')) @@ -193,9 +188,8 @@ public function testItHasEmbedsWithoutIssuedByIfDivisionWebhook() $this->pendingMessage->shouldReceive('flowMeasure')->andReturn($measure); $this->pendingMessage->shouldReceive('reissue')->andReturn($this->reissuer); - $this->pendingMessage->shouldReceive('webhook')->andReturn($this->webhook); $this->reissuer->shouldReceive('isReissuedNotification')->andReturn(false); - $this->webhook->shouldReceive('id')->andReturn(1); + $this->pendingMessage->shouldReceive('isEcfmp')->andReturn(false); $this->assertEquals( [ diff --git a/tests/Discord/FlowMeasure/Generator/EcfmpFlowMeasureMessageGeneratorTest.php b/tests/Discord/FlowMeasure/Generator/EcfmpFlowMeasureMessageGeneratorTest.php new file mode 100644 index 00000000..b0313c8a --- /dev/null +++ b/tests/Discord/FlowMeasure/Generator/EcfmpFlowMeasureMessageGeneratorTest.php @@ -0,0 +1,65 @@ +create(); + $measure2 = FlowMeasure::factory()->create(); + $mockRepository1 = Mockery::mock(RepositoryInterface::class); + $mockRepository1->shouldReceive('notificationType')->andReturn(DiscordNotificationType::FLOW_MEASURE_NOTIFIED); + $mockRepository1->shouldReceive('flowMeasuresToBeSentToEcfmp')->once()->andReturn(collect( + [ + new FlowMeasureForNotification($measure1, true), + new FlowMeasureForNotification($measure2, false), + ] + )); + + $measure3 = FlowMeasure::factory()->create(); + $mockRepository2 = Mockery::mock(RepositoryInterface::class); + $mockRepository2->shouldReceive('notificationType')->andReturn(DiscordNotificationType::FLOW_MEASURE_ACTIVATED); + $mockRepository2->shouldReceive('flowMeasuresToBeSentToEcfmp')->once()->andReturn(collect( + [ + new FlowMeasureForNotification($measure3, true), + ] + )); + + $mockSender = Mockery::mock(EcfmpFlowMeasureSender::class); + + $mockSender->shouldReceive('send')->once()->with(Mockery::on(function (PendingEcfmpMessage $message) use ($measure1) { + return $message->flowMeasure()->id === $measure1->id && + $message->isEcfmp() === true && + $message->type() === DiscordNotificationType::FLOW_MEASURE_NOTIFIED && + $message->reissue()->isReissuedNotification() === true; + })); + + $mockSender->shouldReceive('send')->once()->with(Mockery::on(function (PendingEcfmpMessage $message) use ($measure2) { + return $message->flowMeasure()->id === $measure2->id && + $message->isEcfmp() === true && + $message->type() === DiscordNotificationType::FLOW_MEASURE_NOTIFIED && + $message->reissue()->isReissuedNotification() === false; + })); + + $mockSender->shouldReceive('send')->once()->with(Mockery::on(function (PendingEcfmpMessage $message) use ($measure3) { + return $message->flowMeasure()->id === $measure3->id && + $message->isEcfmp() === true && + $message->type() === DiscordNotificationType::FLOW_MEASURE_ACTIVATED && + $message->reissue()->isReissuedNotification() === true; + })); + + $generator = new EcfmpFlowMeasureMessageGenerator($mockSender, [$mockRepository1, $mockRepository2]); + $generator->generateAndSend(); + } +} diff --git a/tests/Discord/FlowMeasure/Helper/EcfmpNotificationReissuerTest.php b/tests/Discord/FlowMeasure/Helper/EcfmpNotificationReissuerTest.php new file mode 100644 index 00000000..3e09defc --- /dev/null +++ b/tests/Discord/FlowMeasure/Helper/EcfmpNotificationReissuerTest.php @@ -0,0 +1,35 @@ +create(); + $notification = new FlowMeasureForNotification($measure, $isReissued); + $reissuer = new EcfmpNotificationReissuer($notification, $type); + + $this->assertEquals($expected, $reissuer->isReissuedNotification()); + } + + public function reissuingProvider(): array + { + return [ + 'is expired' => [true, DiscordNotificationType::FLOW_MEASURE_EXPIRED, false], + 'is activated but not reissued' => [false, DiscordNotificationType::FLOW_MEASURE_ACTIVATED, false], + 'is notified but not reissued' => [false, DiscordNotificationType::FLOW_MEASURE_NOTIFIED, false], + 'is withdrawn' => [true, DiscordNotificationType::FLOW_MEASURE_WITHDRAWN, false], + 'is notified and reissued' => [true, DiscordNotificationType::FLOW_MEASURE_NOTIFIED, true], + 'is activated and reissued' => [true, DiscordNotificationType::FLOW_MEASURE_ACTIVATED, true], + ]; + } +} diff --git a/tests/Discord/FlowMeasure/Helper/NotificationReissuerTest.php b/tests/Discord/FlowMeasure/Helper/NotificationReissuerTest.php index 7efdb8f3..143ef2a4 100644 --- a/tests/Discord/FlowMeasure/Helper/NotificationReissuerTest.php +++ b/tests/Discord/FlowMeasure/Helper/NotificationReissuerTest.php @@ -5,7 +5,7 @@ use App\Discord\FlowMeasure\Helper\NotificationReissuer; use App\Discord\Webhook\EcfmpWebhook; use App\Enums\DiscordNotificationType as DiscordNotificationTypeEnum; -use App\Models\DiscordNotification; +use App\Models\DivisionDiscordNotification; use App\Models\DiscordNotificationType; use App\Models\DivisionDiscordWebhook; use App\Models\FlowMeasure; @@ -25,11 +25,13 @@ public function testItHasAType() { $this->assertEquals( DiscordNotificationTypeEnum::FLOW_MEASURE_NOTIFIED, - (new NotificationReissuer( - $this->flowMeasure, - DiscordNotificationTypeEnum::FLOW_MEASURE_NOTIFIED, - new EcfmpWebhook() - ))->type() + ( + new NotificationReissuer( + $this->flowMeasure, + DiscordNotificationTypeEnum::FLOW_MEASURE_NOTIFIED, + new EcfmpWebhook() + ) + )->type() ); } @@ -37,18 +39,20 @@ public function testItHasAFlowMeasure() { $this->assertEquals( $this->flowMeasure, - (new NotificationReissuer( - $this->flowMeasure, - DiscordNotificationTypeEnum::FLOW_MEASURE_NOTIFIED, - new EcfmpWebhook() - ))->measure() + ( + new NotificationReissuer( + $this->flowMeasure, + DiscordNotificationTypeEnum::FLOW_MEASURE_NOTIFIED, + new EcfmpWebhook() + ) + )->measure() ); } public function testItsAReissueIfItsNotifiedAndTheIdentifierHasChanged() { - $previousNotification = DiscordNotification::factory()->create(); - $this->flowMeasure->discordNotifications()->sync( + $previousNotification = DivisionDiscordNotification::factory()->create(); + $this->flowMeasure->divisionDiscordNotifications()->sync( [ $previousNotification->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -60,18 +64,20 @@ public function testItsAReissueIfItsNotifiedAndTheIdentifierHasChanged() ); $this->assertTrue( - (new NotificationReissuer( - $this->flowMeasure, - DiscordNotificationTypeEnum::FLOW_MEASURE_NOTIFIED, - new EcfmpWebhook() - ))->isReissuedNotification() + ( + new NotificationReissuer( + $this->flowMeasure, + DiscordNotificationTypeEnum::FLOW_MEASURE_NOTIFIED, + new EcfmpWebhook() + ) + )->isReissuedNotification() ); } public function testItsAReissueIfItsActivatedAndTheIdentifierHasChanged() { - $previousNotification = DiscordNotification::factory()->create(); - $this->flowMeasure->discordNotifications()->sync( + $previousNotification = DivisionDiscordNotification::factory()->create(); + $this->flowMeasure->divisionDiscordNotifications()->sync( [ $previousNotification->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -83,18 +89,20 @@ public function testItsAReissueIfItsActivatedAndTheIdentifierHasChanged() ); $this->assertTrue( - (new NotificationReissuer( - $this->flowMeasure, - DiscordNotificationTypeEnum::FLOW_MEASURE_ACTIVATED, - new EcfmpWebhook() - ))->isReissuedNotification() + ( + new NotificationReissuer( + $this->flowMeasure, + DiscordNotificationTypeEnum::FLOW_MEASURE_ACTIVATED, + new EcfmpWebhook() + ) + )->isReissuedNotification() ); } public function testItsAReissueIfItWasNotifiedAndTheIdentifierHasChangedForActivation() { - $previousNotification = DiscordNotification::factory()->create(); - $this->flowMeasure->discordNotifications()->sync( + $previousNotification = DivisionDiscordNotification::factory()->create(); + $this->flowMeasure->divisionDiscordNotifications()->sync( [ $previousNotification->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -106,25 +114,27 @@ public function testItsAReissueIfItWasNotifiedAndTheIdentifierHasChangedForActiv ); $this->assertTrue( - (new NotificationReissuer( - $this->flowMeasure, - DiscordNotificationTypeEnum::FLOW_MEASURE_ACTIVATED, - new EcfmpWebhook() - ))->isReissuedNotification() + ( + new NotificationReissuer( + $this->flowMeasure, + DiscordNotificationTypeEnum::FLOW_MEASURE_ACTIVATED, + new EcfmpWebhook() + ) + )->isReissuedNotification() ); } public function testItsAReissueIfItsNotifiedOnAEcfmpWebhook() { - $previousNotificationEcfmp = DiscordNotification::factory() + $previousNotificationEcfmp = DivisionDiscordNotification::factory() ->create(); $divisionWebhook = DivisionDiscordWebhook::factory()->create(); - $previousDivisionNotification = DiscordNotification::factory() + $previousDivisionNotification = DivisionDiscordNotification::factory() ->toDivisionWebhook($divisionWebhook) ->create(); - $this->flowMeasure->discordNotifications()->sync( + $this->flowMeasure->divisionDiscordNotifications()->sync( [ $previousNotificationEcfmp->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -142,27 +152,29 @@ public function testItsAReissueIfItsNotifiedOnAEcfmpWebhook() ); $this->assertTrue( - (new NotificationReissuer( - $this->flowMeasure, - DiscordNotificationTypeEnum::FLOW_MEASURE_NOTIFIED, - new EcfmpWebhook() - ))->isReissuedNotification() + ( + new NotificationReissuer( + $this->flowMeasure, + DiscordNotificationTypeEnum::FLOW_MEASURE_NOTIFIED, + new EcfmpWebhook() + ) + )->isReissuedNotification() ); } public function testItsAReissueIfItsNotifiedOnADifferentDivisionWebhook() { $otherDivisionWebhook = DivisionDiscordWebhook::factory()->create(); - $previousNotificationOtherDivision = DiscordNotification::factory() + $previousNotificationOtherDivision = DivisionDiscordNotification::factory() ->toDivisionWebhook($otherDivisionWebhook) ->create(); $divisionWebhook = DivisionDiscordWebhook::factory()->create(); - $previousDivisionNotification = DiscordNotification::factory() + $previousDivisionNotification = DivisionDiscordNotification::factory() ->toDivisionWebhook($divisionWebhook) ->create(); - $this->flowMeasure->discordNotifications()->sync( + $this->flowMeasure->divisionDiscordNotifications()->sync( [ $previousNotificationOtherDivision->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -180,27 +192,29 @@ public function testItsAReissueIfItsNotifiedOnADifferentDivisionWebhook() ); $this->assertTrue( - (new NotificationReissuer( - $this->flowMeasure, - DiscordNotificationTypeEnum::FLOW_MEASURE_NOTIFIED, - $divisionWebhook - ))->isReissuedNotification() + ( + new NotificationReissuer( + $this->flowMeasure, + DiscordNotificationTypeEnum::FLOW_MEASURE_NOTIFIED, + $divisionWebhook + ) + )->isReissuedNotification() ); } public function testItsAReissueIfItsActivatedOnADifferentDivisionWebhook() { $otherDivisionWebhook = DivisionDiscordWebhook::factory()->create(); - $previousNotificationOtherDivision = DiscordNotification::factory() + $previousNotificationOtherDivision = DivisionDiscordNotification::factory() ->toDivisionWebhook($otherDivisionWebhook) ->create(); $divisionWebhook = DivisionDiscordWebhook::factory()->create(); - $previousDivisionNotification = DiscordNotification::factory() + $previousDivisionNotification = DivisionDiscordNotification::factory() ->toDivisionWebhook($divisionWebhook) ->create(); - $this->flowMeasure->discordNotifications()->sync( + $this->flowMeasure->divisionDiscordNotifications()->sync( [ $previousNotificationOtherDivision->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -218,20 +232,22 @@ public function testItsAReissueIfItsActivatedOnADifferentDivisionWebhook() ); $this->assertTrue( - (new NotificationReissuer( - $this->flowMeasure, - DiscordNotificationTypeEnum::FLOW_MEASURE_ACTIVATED, - $divisionWebhook - ))->isReissuedNotification() + ( + new NotificationReissuer( + $this->flowMeasure, + DiscordNotificationTypeEnum::FLOW_MEASURE_ACTIVATED, + $divisionWebhook + ) + )->isReissuedNotification() ); } public function testItsAReissueIfItsActivatedOnAEcfmpWebhook() { - $previousNotification = DiscordNotification::factory() + $previousNotification = DivisionDiscordNotification::factory() ->create(); - $this->flowMeasure->discordNotifications()->sync( + $this->flowMeasure->divisionDiscordNotifications()->sync( [ $previousNotification->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -243,18 +259,20 @@ public function testItsAReissueIfItsActivatedOnAEcfmpWebhook() ); $this->assertTrue( - (new NotificationReissuer( - $this->flowMeasure, - DiscordNotificationTypeEnum::FLOW_MEASURE_NOTIFIED, - new EcfmpWebhook() - ))->isReissuedNotification() + ( + new NotificationReissuer( + $this->flowMeasure, + DiscordNotificationTypeEnum::FLOW_MEASURE_NOTIFIED, + new EcfmpWebhook() + ) + )->isReissuedNotification() ); } public function testItIsNotAReissueIfItsNotifiedAndTheIdentifierHasNotChanged() { - $previousNotification = DiscordNotification::factory()->create(); - $this->flowMeasure->discordNotifications()->sync( + $previousNotification = DivisionDiscordNotification::factory()->create(); + $this->flowMeasure->divisionDiscordNotifications()->sync( [ $previousNotification->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -266,18 +284,20 @@ public function testItIsNotAReissueIfItsNotifiedAndTheIdentifierHasNotChanged() ); $this->assertFalse( - (new NotificationReissuer( - $this->flowMeasure, - DiscordNotificationTypeEnum::FLOW_MEASURE_NOTIFIED, - new EcfmpWebhook() - ))->isReissuedNotification() + ( + new NotificationReissuer( + $this->flowMeasure, + DiscordNotificationTypeEnum::FLOW_MEASURE_NOTIFIED, + new EcfmpWebhook() + ) + )->isReissuedNotification() ); } public function testItIsNotAReissueIfItsActivatedAndTheIdentifierHasNotChanged() { - $previousNotification = DiscordNotification::factory()->create(); - $this->flowMeasure->discordNotifications()->sync( + $previousNotification = DivisionDiscordNotification::factory()->create(); + $this->flowMeasure->divisionDiscordNotifications()->sync( [ $previousNotification->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -289,18 +309,20 @@ public function testItIsNotAReissueIfItsActivatedAndTheIdentifierHasNotChanged() ); $this->assertFalse( - (new NotificationReissuer( - $this->flowMeasure, - DiscordNotificationTypeEnum::FLOW_MEASURE_ACTIVATED, - new EcfmpWebhook() - ))->isReissuedNotification() + ( + new NotificationReissuer( + $this->flowMeasure, + DiscordNotificationTypeEnum::FLOW_MEASURE_ACTIVATED, + new EcfmpWebhook() + ) + )->isReissuedNotification() ); } public function testItIsNotAReissueIfItsNotifiedAndThenActivatedTheIdentifierHasNotChanged() { - $previousNotification = DiscordNotification::factory()->create(); - $this->flowMeasure->discordNotifications()->sync( + $previousNotification = DivisionDiscordNotification::factory()->create(); + $this->flowMeasure->divisionDiscordNotifications()->sync( [ $previousNotification->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -312,40 +334,46 @@ public function testItIsNotAReissueIfItsNotifiedAndThenActivatedTheIdentifierHas ); $this->assertFalse( - (new NotificationReissuer( - $this->flowMeasure, - DiscordNotificationTypeEnum::FLOW_MEASURE_ACTIVATED, - new EcfmpWebhook() - ))->isReissuedNotification() + ( + new NotificationReissuer( + $this->flowMeasure, + DiscordNotificationTypeEnum::FLOW_MEASURE_ACTIVATED, + new EcfmpWebhook() + ) + )->isReissuedNotification() ); } public function testItIsNotAReissueIfItsNotifiedAndNeverBeenNotified() { $this->assertFalse( - (new NotificationReissuer( - $this->flowMeasure, - DiscordNotificationTypeEnum::FLOW_MEASURE_NOTIFIED, - new EcfmpWebhook() - ))->isReissuedNotification() + ( + new NotificationReissuer( + $this->flowMeasure, + DiscordNotificationTypeEnum::FLOW_MEASURE_NOTIFIED, + new EcfmpWebhook() + ) + )->isReissuedNotification() ); } public function testItIsNotAReissueIfItsNotifiedAndNeverBeenActivated() { $this->assertFalse( - (new NotificationReissuer( - $this->flowMeasure, - DiscordNotificationTypeEnum::FLOW_MEASURE_ACTIVATED, - new EcfmpWebhook() - ))->isReissuedNotification() + ( + new NotificationReissuer( + $this->flowMeasure, + DiscordNotificationTypeEnum::FLOW_MEASURE_ACTIVATED, + new EcfmpWebhook() + ) + )->isReissuedNotification() ); } public function testItIsNotAReissueIfItsWithdrawn() { - $previousNotification = DiscordNotification::factory()->create(); - $this->flowMeasure->discordNotifications()->sync( + $previousNotification = DivisionDiscordNotification::factory()->create(); + $this->flowMeasure->divisionDiscordNotifications()->sync( [ $previousNotification->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -357,18 +385,20 @@ public function testItIsNotAReissueIfItsWithdrawn() ); $this->assertFalse( - (new NotificationReissuer( - $this->flowMeasure, - DiscordNotificationTypeEnum::FLOW_MEASURE_WITHDRAWN, - new EcfmpWebhook() - ))->isReissuedNotification() + ( + new NotificationReissuer( + $this->flowMeasure, + DiscordNotificationTypeEnum::FLOW_MEASURE_WITHDRAWN, + new EcfmpWebhook() + ) + )->isReissuedNotification() ); } public function testItIsNotAReissueIfItsExpired() { - $previousNotification = DiscordNotification::factory()->create(); - $this->flowMeasure->discordNotifications()->sync( + $previousNotification = DivisionDiscordNotification::factory()->create(); + $this->flowMeasure->divisionDiscordNotifications()->sync( [ $previousNotification->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -380,11 +410,13 @@ public function testItIsNotAReissueIfItsExpired() ); $this->assertFalse( - (new NotificationReissuer( - $this->flowMeasure, - DiscordNotificationTypeEnum::FLOW_MEASURE_EXPIRED, - new EcfmpWebhook() - ))->isReissuedNotification() + ( + new NotificationReissuer( + $this->flowMeasure, + DiscordNotificationTypeEnum::FLOW_MEASURE_EXPIRED, + new EcfmpWebhook() + ) + )->isReissuedNotification() ); } } diff --git a/tests/Discord/FlowMeasure/Logger/FlowMeasureLoggerTest.php b/tests/Discord/FlowMeasure/Logger/FlowMeasureLoggerTest.php index cd81c0a8..c3e88bb7 100644 --- a/tests/Discord/FlowMeasure/Logger/FlowMeasureLoggerTest.php +++ b/tests/Discord/FlowMeasure/Logger/FlowMeasureLoggerTest.php @@ -4,7 +4,7 @@ use App\Discord\FlowMeasure\Logger\FlowMeasureLogger; use App\Enums\DiscordNotificationType as DiscordNotificationTypeEnum; -use App\Models\DiscordNotification; +use App\Models\DivisionDiscordNotification; use App\Models\DivisionDiscordWebhook; use App\Models\FlowMeasure; use Tests\TestCase; @@ -13,7 +13,7 @@ class FlowMeasureLoggerTest extends TestCase { public function testItLogsTheNotificationForEcfmp() { - $notification = DiscordNotification::factory()->create(); + $notification = DivisionDiscordNotification::factory()->create(); $measure = FlowMeasure::factory()->create(); $logger = new FlowMeasureLogger($measure, DiscordNotificationTypeEnum::FLOW_MEASURE_ACTIVATED); @@ -24,7 +24,7 @@ public function testItLogsTheNotificationForEcfmp() [ 'log_name' => 'Discord', 'description' => 'Sending discord notification', - 'subject_type' => 'App\Models\DiscordNotification', + 'subject_type' => 'App\Models\DivisionDiscordNotification', 'event' => $measure->identifier . ' - Activated', ] ); @@ -33,7 +33,7 @@ public function testItLogsTheNotificationForEcfmp() public function testItLogsTheNotificationForDivisions() { $webhook = DivisionDiscordWebhook::factory()->create(); - $notification = DiscordNotification::factory()->toDivisionWebhook($webhook)->create(); + $notification = DivisionDiscordNotification::factory()->toDivisionWebhook($webhook)->create(); $measure = FlowMeasure::factory()->create(); $logger = new FlowMeasureLogger($measure, DiscordNotificationTypeEnum::FLOW_MEASURE_ACTIVATED); @@ -44,7 +44,7 @@ public function testItLogsTheNotificationForDivisions() [ 'log_name' => 'Discord', 'description' => 'Sending discord notification', - 'subject_type' => 'App\Models\DiscordNotification', + 'subject_type' => 'App\Models\DivisionDiscordNotification', 'event' => $measure->identifier . ' - Activated', ] ); diff --git a/tests/Discord/FlowMeasure/Message/FlowMeasureMessageFactoryTest.php b/tests/Discord/FlowMeasure/Message/FlowMeasureMessageFactoryTest.php index 9a0e666a..68f3e366 100644 --- a/tests/Discord/FlowMeasure/Message/FlowMeasureMessageFactoryTest.php +++ b/tests/Discord/FlowMeasure/Message/FlowMeasureMessageFactoryTest.php @@ -8,6 +8,7 @@ use App\Discord\FlowMeasure\Embed\FlowMeasureEmbedInterface; use App\Discord\FlowMeasure\Message\FlowMeasureMessageFactory; use App\Discord\FlowMeasure\Provider\PendingMessageInterface; +use App\Discord\FlowMeasure\Provider\PendingWebhookMessageInterface; use App\Discord\Message\Embed\EmbedCollection; use App\Discord\Webhook\WebhookInterface; use App\Enums\DiscordNotificationType; @@ -23,18 +24,20 @@ class FlowMeasureMessageFactoryTest extends TestCase private readonly FlowMeasureRecipientsInterface $recipients; private readonly FlowMeasureRecipientsFactory $recipientsFactory; private readonly FlowMeasureMessageFactory $factory; - private readonly PendingMessageInterface $pendingMessage; + private readonly PendingWebhookMessageInterface $pendingWebhookMessage; private readonly WebhookInterface $webhook; + private readonly PendingMessageInterface $pendingEcfmpMessage; + public function setUp(): void { parent::setUp(); $flowMeasure = FlowMeasure::factory()->make(); $this->webhook = Mockery::mock(WebhookInterface::class); - $this->pendingMessage = Mockery::mock(PendingMessageInterface::class); - $this->pendingMessage->shouldReceive('webhook')->andReturn($this->webhook); - $this->pendingMessage->shouldReceive('flowMeasure')->andReturn($flowMeasure); - $this->pendingMessage->shouldReceive('type')->andReturn(DiscordNotificationType::FLOW_MEASURE_WITHDRAWN); + $this->pendingWebhookMessage = Mockery::mock(PendingWebhookMessageInterface::class); + $this->pendingWebhookMessage->shouldReceive('webhook')->andReturn($this->webhook); + $this->pendingWebhookMessage->shouldReceive('flowMeasure')->andReturn($flowMeasure); + $this->pendingWebhookMessage->shouldReceive('type')->andReturn(DiscordNotificationType::FLOW_MEASURE_WITHDRAWN); $this->embedCollection = new EmbedCollection(); $this->embeds = Mockery::mock(FlowMeasureEmbedInterface::class); $this->embeds->shouldReceive('embeds')->andReturn($this->embedCollection); @@ -45,13 +48,25 @@ public function setUp(): void $this->recipientsFactory = Mockery::mock(FlowMeasureRecipientsFactory::class); $this->recipientsFactory->shouldReceive('makeRecipients')->andReturn($this->recipients); $this->factory = new FlowMeasureMessageFactory($this->recipientsFactory, $this->embedFactory); + $this->pendingEcfmpMessage = Mockery::mock(PendingMessageInterface::class); + $this->pendingEcfmpMessage->shouldReceive('flowMeasure')->andReturn(FlowMeasure::factory()->make()); + $this->pendingEcfmpMessage->shouldReceive('type')->andReturn(DiscordNotificationType::FLOW_MEASURE_WITHDRAWN); + $this->recipientsFactory->shouldReceive('makeEcfmpRecipients')->andReturn($this->recipients); } public function testItMakesAMessage() { - $message = $this->factory->make($this->pendingMessage); + $message = $this->factory->make($this->pendingWebhookMessage); $this->assertEquals($this->webhook, $message->destination()); $this->assertEquals('foo', $message->content()); $this->assertEquals($this->embedCollection, $message->embeds()); } + + public function testItMakesAnEcfmpMessage() + { + $message = $this->factory->makeEcfmp($this->pendingEcfmpMessage); + $this->assertEquals(config('discord.ecfmp_channel_id'), $message->channel()); + $this->assertEquals('foo', $message->content()); + $this->assertEquals($this->embedCollection, $message->embeds()); + } } diff --git a/tests/Discord/FlowMeasure/Message/MessageGeneratorTest.php b/tests/Discord/FlowMeasure/Message/MessageGeneratorTest.php index 6bfe6352..b947a848 100644 --- a/tests/Discord/FlowMeasure/Message/MessageGeneratorTest.php +++ b/tests/Discord/FlowMeasure/Message/MessageGeneratorTest.php @@ -6,7 +6,7 @@ use App\Discord\FlowMeasure\Message\FlowMeasureMessageFactory; use App\Discord\FlowMeasure\Message\MessageGenerator; use App\Discord\FlowMeasure\Provider\MessageProviderInterface; -use App\Discord\FlowMeasure\Provider\PendingMessageInterface; +use App\Discord\FlowMeasure\Provider\PendingWebhookMessageInterface; use Mockery; use Tests\TestCase; @@ -16,8 +16,8 @@ public function testItGeneratesMessages() { $provider = Mockery::mock(MessageProviderInterface::class); $measureFactory = Mockery::mock(FlowMeasureMessageFactory::class); - $message1 = Mockery::mock(PendingMessageInterface::class); - $message2 = Mockery::mock(PendingMessageInterface::class); + $message1 = Mockery::mock(PendingWebhookMessageInterface::class); + $message2 = Mockery::mock(PendingWebhookMessageInterface::class); $flowMeasureMessage1 = Mockery::mock(FlowMeasureMessage::class); $flowMeasureMessage2 = Mockery::mock(FlowMeasureMessage::class); diff --git a/tests/Discord/FlowMeasure/Provider/MessageProviderTest.php b/tests/Discord/FlowMeasure/Provider/DivisionWebhookMessageProviderTest.php similarity index 90% rename from tests/Discord/FlowMeasure/Provider/MessageProviderTest.php rename to tests/Discord/FlowMeasure/Provider/DivisionWebhookMessageProviderTest.php index 02d5a052..8cc93efd 100644 --- a/tests/Discord/FlowMeasure/Provider/MessageProviderTest.php +++ b/tests/Discord/FlowMeasure/Provider/DivisionWebhookMessageProviderTest.php @@ -2,7 +2,7 @@ namespace Tests\Discord\FlowMeasure\Provider; -use App\Discord\FlowMeasure\Provider\MessageProvider; +use App\Discord\FlowMeasure\Provider\DivisionWebhookMessageProvider; use App\Discord\FlowMeasure\Webhook\WebhookMapper; use App\Enums\DiscordNotificationType; use App\Models\DivisionDiscordWebhook; @@ -11,18 +11,18 @@ use Mockery; use Tests\TestCase; -class MessageProviderTest extends TestCase +class DivisionWebhookMessageProviderTest extends TestCase { private readonly RepositoryInterface $repository; private readonly WebhookMapper $mapper; - private readonly MessageProvider $messageProvider; + private readonly DivisionWebhookMessageProvider $messageProvider; public function setUp(): void { parent::setUp(); $this->repository = Mockery::mock(RepositoryInterface::class); $this->mapper = Mockery::mock(WebhookMapper::class); - $this->messageProvider = new MessageProvider($this->repository, $this->mapper); + $this->messageProvider = new DivisionWebhookMessageProvider($this->repository, $this->mapper); } public function testItProvidesPendingMessages() diff --git a/app/Discord/FlowMeasure/Provider/PendingDiscordMessageTest.php b/tests/Discord/FlowMeasure/Provider/PendingDiscordWebhookMessageTest.php similarity index 73% rename from app/Discord/FlowMeasure/Provider/PendingDiscordMessageTest.php rename to tests/Discord/FlowMeasure/Provider/PendingDiscordWebhookMessageTest.php index c8c6fdd5..06275ced 100644 --- a/app/Discord/FlowMeasure/Provider/PendingDiscordMessageTest.php +++ b/tests/Discord/FlowMeasure/Provider/PendingDiscordWebhookMessageTest.php @@ -1,20 +1,21 @@ type = DiscordNotificationType::FLOW_MEASURE_ACTIVATED; $this->webhook = Mockery::mock(WebhookInterface::class); $this->reissue = Mockery::mock(NotificationReissuerInterface::class); - $this->message = new PendingDiscordMessage($this->measure, $this->type, $this->webhook, $this->reissue); + $this->message = new PendingDiscordWebhookMessage($this->measure, $this->type, $this->webhook, $this->reissue); } public function testItHasAMeasure() @@ -46,4 +47,9 @@ public function testItIsReissued() { $this->assertEquals($this->reissue, $this->message->reissue()); } + + public function testItIsNotEcfmp() + { + $this->assertFalse($this->message->isEcfmp()); + } } diff --git a/tests/Discord/FlowMeasure/Provider/PendingEcfmpMessageTest.php b/tests/Discord/FlowMeasure/Provider/PendingEcfmpMessageTest.php new file mode 100644 index 00000000..77bc5d7d --- /dev/null +++ b/tests/Discord/FlowMeasure/Provider/PendingEcfmpMessageTest.php @@ -0,0 +1,47 @@ +measure = FlowMeasure::factory()->create(); + $this->type = DiscordNotificationType::FLOW_MEASURE_ACTIVATED; + $this->reissue = Mockery::mock(NotificationReissuerInterface::class); + $this->message = new PendingEcfmpMessage($this->measure, $this->type, $this->reissue); + } + + public function testItHasAMeasure() + { + $this->assertEquals($this->measure, $this->message->flowMeasure()); + } + + public function testItHasAType() + { + $this->assertEquals($this->type, $this->message->type()); + } + + public function testItIsReissued() + { + $this->assertEquals($this->reissue, $this->message->reissue()); + } + + public function testItIsEcfmp() + { + $this->assertTrue($this->message->isEcfmp()); + } +} diff --git a/tests/Discord/FlowMeasure/Sender/EcfmpFlowMeasureSenderTest.php b/tests/Discord/FlowMeasure/Sender/EcfmpFlowMeasureSenderTest.php new file mode 100644 index 00000000..de6a868d --- /dev/null +++ b/tests/Discord/FlowMeasure/Sender/EcfmpFlowMeasureSenderTest.php @@ -0,0 +1,88 @@ +discordService = Mockery::mock(DiscordServiceInterface::class); + $this->messageFactory = Mockery::mock(FlowMeasureMessageFactory::class); + $this->app->instance(DiscordServiceInterface::class, $this->discordService); + $this->app->instance(FlowMeasureMessageFactory::class, $this->messageFactory); + + Config::set('discord.client_request_app_id', 'testabc'); + $this->sender = $this->app->make(EcfmpFlowMeasureSender::class); + } + + public function testItSendsTheMessage(): void + { + $flowMeasure = FlowMeasure::factory()->create(); + $pendingMessage = new PendingEcfmpMessage($flowMeasure, DiscordNotificationType::FLOW_MEASURE_ACTIVATED, Mockery::mock(NotificationReissuerInterface::class)); + $flowMeasureMessage = new EcfmpFlowMeasureMessage('test-abc', Mockery::mock(FlowMeasureRecipientsInterface::class), Mockery::mock(FlowMeasureEmbedInterface::class)); + + $this->messageFactory->shouldReceive('makeEcfmp')->once()->with($pendingMessage)->andReturn($flowMeasureMessage); + + $expectedClientRequestId = 'testabc-' . DiscordNotificationType::FLOW_MEASURE_ACTIVATED->value . '-' . $flowMeasure->id . '-' . $flowMeasure->identifier; + + $this->discordService->shouldReceive('sendMessage')->once()->with($expectedClientRequestId, $flowMeasureMessage)->andReturn('1234567890'); + + $this->sender->send($pendingMessage); + + $notification = DiscordNotification::latest()->first(); + $this->assertEquals('1234567890', $notification->remote_id); + + $this->assertDatabaseHas('discord_notification_flow_measure', [ + 'discord_notification_type_id' => DiscordNotificationTypeModel::idFromEnum(DiscordNotificationType::FLOW_MEASURE_ACTIVATED), + 'notified_as' => $flowMeasure->identifier, + 'discord_notification_id' => $notification->id, + 'flow_measure_id' => $flowMeasure->id, + ]); + } + + public function testItHandlesExceptionIfSendingFails(): void + { + $startNotificationCount = DiscordNotification::count(); + + $flowMeasure = FlowMeasure::factory()->create(); + $pendingMessage = new PendingEcfmpMessage($flowMeasure, DiscordNotificationType::FLOW_MEASURE_ACTIVATED, Mockery::mock(NotificationReissuerInterface::class)); + $flowMeasureMessage = new EcfmpFlowMeasureMessage('test-abc', Mockery::mock(FlowMeasureRecipientsInterface::class), Mockery::mock(FlowMeasureEmbedInterface::class)); + + $this->messageFactory->shouldReceive('makeEcfmp')->once()->with($pendingMessage)->andReturn($flowMeasureMessage); + + $expectedClientRequestId = 'testabc-' . DiscordNotificationType::FLOW_MEASURE_ACTIVATED->value . '-' . $flowMeasure->id . '-' . $flowMeasure->identifier; + + $this->discordService->shouldReceive('sendMessage')->once()->with($expectedClientRequestId, $flowMeasureMessage)->andThrow(new DiscordServiceException('test')); + + $this->sender->send($pendingMessage); + + + $this->assertDatabaseCount('discord_notifications', $startNotificationCount); + $this->assertDatabaseMissing('discord_notification_flow_measure', [ + 'flow_measure_id' => $flowMeasure->id, + ]); + } +} diff --git a/tests/Discord/FlowMeasure/Webhook/Filter/ActivatedWebhookFilterTest.php b/tests/Discord/FlowMeasure/Webhook/Filter/ActivatedWebhookFilterTest.php index d194150f..94f9877f 100644 --- a/tests/Discord/FlowMeasure/Webhook/Filter/ActivatedWebhookFilterTest.php +++ b/tests/Discord/FlowMeasure/Webhook/Filter/ActivatedWebhookFilterTest.php @@ -5,7 +5,7 @@ use App\Discord\FlowMeasure\Webhook\Filter\ActivatedWebhookFilter; use App\Discord\Webhook\EcfmpWebhook; use App\Enums\DiscordNotificationType as DiscordNotificationTypeEnum; -use App\Models\DiscordNotification; +use App\Models\DivisionDiscordNotification; use App\Models\DiscordNotificationType; use App\Models\DivisionDiscordWebhook; use App\Models\FlowMeasure; @@ -39,8 +39,8 @@ public function testItShouldUseWebhookIfFlowMeasureHasNeverBeenActivatedToEcfmpW public function testItShouldUseWebhookIfFlowMeasureHasOnlyBeenNotifiedToEcfmpWebhook() { $measure = FlowMeasure::factory()->create(); - $discordNotification = DiscordNotification::factory()->create(); - $measure->discordNotifications()->attach( + $discordNotification = DivisionDiscordNotification::factory()->create(); + $measure->divisionDiscordNotifications()->attach( [ $discordNotification->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -62,8 +62,8 @@ public function testItShouldUseWebhookIfFlowMeasureHasOnlyBeenNotifiedToEcfmpWeb public function testItShouldUseWebhookIfFlowMeasureHasBeenActivatedAsDifferentIdentifierToEcfmpWebhook() { $measure = FlowMeasure::factory()->create(); - $discordNotification = DiscordNotification::factory()->create(); - $measure->discordNotifications()->attach( + $discordNotification = DivisionDiscordNotification::factory()->create(); + $measure->divisionDiscordNotifications()->attach( [ $discordNotification->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -85,8 +85,8 @@ public function testItShouldUseWebhookIfFlowMeasureHasBeenActivatedAsDifferentId public function testItShouldNotUseWebhookIfFlowMeasureHasBeenActivatedToEcfmpWebhook() { $measure = FlowMeasure::factory()->create(); - $discordNotification = DiscordNotification::factory()->create(); - $measure->discordNotifications()->attach( + $discordNotification = DivisionDiscordNotification::factory()->create(); + $measure->divisionDiscordNotifications()->attach( [ $discordNotification->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -119,10 +119,10 @@ public function testItShouldUseWebhookIfFlowMeasureHasNeverBeenActivatedToDivisi public function testItShouldUseWebhookIfFlowMeasureHasOnlyBeenNotifiedToDivisionWebhook() { $measure = FlowMeasure::factory()->create(); - $discordNotification = DiscordNotification::factory() + $discordNotification = DivisionDiscordNotification::factory() ->toDivisionWebhook($this->divisionDiscordWebhook) ->create(); - $measure->discordNotifications()->attach( + $measure->divisionDiscordNotifications()->attach( [ $discordNotification->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -144,10 +144,10 @@ public function testItShouldUseWebhookIfFlowMeasureHasOnlyBeenNotifiedToDivision public function testItShouldUseWebhookIfFlowMeasureHasBeenActivatedAsDifferentIdentifierToDivisionWebhook() { $measure = FlowMeasure::factory()->create(); - $discordNotification = DiscordNotification::factory() + $discordNotification = DivisionDiscordNotification::factory() ->toDivisionWebhook($this->divisionDiscordWebhook) ->create(); - $measure->discordNotifications()->attach( + $measure->divisionDiscordNotifications()->attach( [ $discordNotification->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -169,10 +169,10 @@ public function testItShouldUseWebhookIfFlowMeasureHasBeenActivatedAsDifferentId public function testItShouldNotUseWebhookIfFlowMeasureHasBeenActivatedToDivisionWebhook() { $measure = FlowMeasure::factory()->create(); - $discordNotification = DiscordNotification::factory() + $discordNotification = DivisionDiscordNotification::factory() ->toDivisionWebhook($this->divisionDiscordWebhook) ->create(); - $measure->discordNotifications()->attach( + $measure->divisionDiscordNotifications()->attach( [ $discordNotification->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( diff --git a/tests/Discord/FlowMeasure/Webhook/Filter/ExpiredWebhookFilterTest.php b/tests/Discord/FlowMeasure/Webhook/Filter/ExpiredWebhookFilterTest.php index cad33a6d..13ce3250 100644 --- a/tests/Discord/FlowMeasure/Webhook/Filter/ExpiredWebhookFilterTest.php +++ b/tests/Discord/FlowMeasure/Webhook/Filter/ExpiredWebhookFilterTest.php @@ -5,7 +5,7 @@ use App\Discord\FlowMeasure\Webhook\Filter\ExpiredWebhookFilter; use App\Discord\Webhook\EcfmpWebhook; use App\Enums\DiscordNotificationType as DiscordNotificationTypeEnum; -use App\Models\DiscordNotification; +use App\Models\DivisionDiscordNotification; use App\Models\DiscordNotificationType; use App\Models\DivisionDiscordWebhook; use App\Models\FlowMeasure; @@ -29,7 +29,7 @@ public function setUp(): void public function testItShouldUseEcfmpWebhookIfLotsOfNotificationsHaveBeenSentRecently() { $measure = FlowMeasure::factory()->create(); - DiscordNotification::factory()->count(6)->create(['division_discord_webhook_id' => null]); + DivisionDiscordNotification::factory()->count(6)->create(['division_discord_webhook_id' => null]); $this->assertTrue( $this->filter->shouldUseWebhook( @@ -71,8 +71,8 @@ public function testItShouldUseEcfmpWebhookIfThereAreOtherMeasuresActiveAroundTh public function testItShouldUseWebhookIfFlowMeasureHasOnlyBeenNotifiedToEcfmpWebhook() { $measure = FlowMeasure::factory()->create(['identifier' => 'EGTT23A-3']); - $discordNotification = DiscordNotification::factory()->create(); - $measure->discordNotifications()->attach( + $discordNotification = DivisionDiscordNotification::factory()->create(); + $measure->divisionDiscordNotifications()->attach( [ $discordNotification->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -94,8 +94,8 @@ public function testItShouldUseWebhookIfFlowMeasureHasOnlyBeenNotifiedToEcfmpWeb public function testItShouldUseWebhookIfFlowMeasureHasOnlyBeenActivatedToEcfmpWebhook() { $measure = FlowMeasure::factory()->create(['identifier' => 'EGTT23A-3']); - $discordNotification = DiscordNotification::factory()->create(); - $measure->discordNotifications()->attach( + $discordNotification = DivisionDiscordNotification::factory()->create(); + $measure->divisionDiscordNotifications()->attach( [ $discordNotification->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -117,8 +117,8 @@ public function testItShouldUseWebhookIfFlowMeasureHasOnlyBeenActivatedToEcfmpWe public function testItShouldNotUseWebhookIfFlowMeasureHasBeenWithdrawnToEcfmpWebhook() { $measure = FlowMeasure::factory()->create(['identifier' => 'EGTT23A-3']); - $discordNotification = DiscordNotification::factory()->create(); - $measure->discordNotifications()->attach( + $discordNotification = DivisionDiscordNotification::factory()->create(); + $measure->divisionDiscordNotifications()->attach( [ $discordNotification->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -140,8 +140,8 @@ public function testItShouldNotUseWebhookIfFlowMeasureHasBeenWithdrawnToEcfmpWeb public function testItShouldNotUseWebhookIfFlowMeasureHasBeenExpiredToEcfmpWebhook() { $measure = FlowMeasure::factory()->create(['identifier' => 'EGTT23A-3']); - $discordNotification = DiscordNotification::factory()->create(); - $measure->discordNotifications()->attach( + $discordNotification = DivisionDiscordNotification::factory()->create(); + $measure->divisionDiscordNotifications()->attach( [ $discordNotification->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -172,8 +172,8 @@ public function testItShouldNotUseWebhookIfDoesntMeetConditionsForEcfmpWebhook() ->create(); // Not too many recently sent - DiscordNotification::factory()->count(5)->create(['division_discord_webhook_id' => null]); - DiscordNotification::factory()->count(5) + DivisionDiscordNotification::factory()->count(5)->create(['division_discord_webhook_id' => null]); + DivisionDiscordNotification::factory()->count(5) ->toDivisionWebhook(DivisionDiscordWebhook::factory()->create()) ->create(); @@ -188,10 +188,10 @@ public function testItShouldNotUseWebhookIfDoesntMeetConditionsForEcfmpWebhook() public function testItShouldNotUseWebhookIfFlowMeasureHasOnlyBeenNotifiedToDivisionWebhook() { $measure = FlowMeasure::factory()->create(); - $discordNotification = DiscordNotification::factory() + $discordNotification = DivisionDiscordNotification::factory() ->toDivisionWebhook($this->divisionDiscordWebhook) ->create(); - $measure->discordNotifications()->attach( + $measure->divisionDiscordNotifications()->attach( [ $discordNotification->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -213,10 +213,10 @@ public function testItShouldNotUseWebhookIfFlowMeasureHasOnlyBeenNotifiedToDivis public function testItShouldNotUseWebhookIfFlowMeasureHasOnlyBeenActivatedToDivisionWebhook() { $measure = FlowMeasure::factory()->create(); - $discordNotification = DiscordNotification::factory() + $discordNotification = DivisionDiscordNotification::factory() ->toDivisionWebhook($this->divisionDiscordWebhook) ->create(); - $measure->discordNotifications()->attach( + $measure->divisionDiscordNotifications()->attach( [ $discordNotification->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -238,10 +238,10 @@ public function testItShouldNotUseWebhookIfFlowMeasureHasOnlyBeenActivatedToDivi public function testItShouldNotUseWebhookIfFlowMeasureHasBeenExpiredToDivisionWebhook() { $measure = FlowMeasure::factory()->create(); - $discordNotification = DiscordNotification::factory() + $discordNotification = DivisionDiscordNotification::factory() ->toDivisionWebhook($this->divisionDiscordWebhook) ->create(); - $measure->discordNotifications()->attach( + $measure->divisionDiscordNotifications()->attach( [ $discordNotification->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -263,10 +263,10 @@ public function testItShouldNotUseWebhookIfFlowMeasureHasBeenExpiredToDivisionWe public function testItShouldNotUseWebhookIfFlowMeasureHasBeenWithdrawnToDivisionWebhook() { $measure = FlowMeasure::factory()->create(); - $discordNotification = DiscordNotification::factory() + $discordNotification = DivisionDiscordNotification::factory() ->toDivisionWebhook($this->divisionDiscordWebhook) ->create(); - $measure->discordNotifications()->attach( + $measure->divisionDiscordNotifications()->attach( [ $discordNotification->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( diff --git a/tests/Discord/FlowMeasure/Webhook/Filter/NotifiedWebhookFilterTest.php b/tests/Discord/FlowMeasure/Webhook/Filter/NotifiedWebhookFilterTest.php index 0469021d..fbace8a0 100644 --- a/tests/Discord/FlowMeasure/Webhook/Filter/NotifiedWebhookFilterTest.php +++ b/tests/Discord/FlowMeasure/Webhook/Filter/NotifiedWebhookFilterTest.php @@ -5,7 +5,7 @@ use App\Discord\FlowMeasure\Webhook\Filter\NotifiedWebhookFilter; use App\Discord\Webhook\EcfmpWebhook; use App\Enums\DiscordNotificationType as DiscordNotificationTypeEnum; -use App\Models\DiscordNotification; +use App\Models\DivisionDiscordNotification; use App\Models\DiscordNotificationType; use App\Models\DivisionDiscordWebhook; use App\Models\FlowMeasure; @@ -39,8 +39,8 @@ public function testItShouldUseWebhookIfFlowMeasureHasNeverBeenActivatedOrNotifi public function testItShouldUseWebhookIfFlowMeasureHasBeenNotifiedToEcfmpWebhookUnderDifferentIdentifier() { $measure = FlowMeasure::factory()->create(); - $discordNotification = DiscordNotification::factory()->create(); - $measure->discordNotifications()->attach( + $discordNotification = DivisionDiscordNotification::factory()->create(); + $measure->divisionDiscordNotifications()->attach( [ $discordNotification->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -62,8 +62,8 @@ public function testItShouldUseWebhookIfFlowMeasureHasBeenNotifiedToEcfmpWebhook public function testItShouldNotUseWebhookIfFlowMeasureHasBeenNotifiedToEcfmpWebhook() { $measure = FlowMeasure::factory()->create(); - $discordNotification = DiscordNotification::factory()->create(); - $measure->discordNotifications()->attach( + $discordNotification = DivisionDiscordNotification::factory()->create(); + $measure->divisionDiscordNotifications()->attach( [ $discordNotification->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -85,8 +85,8 @@ public function testItShouldNotUseWebhookIfFlowMeasureHasBeenNotifiedToEcfmpWebh public function testItShouldNotUseWebhookIfFlowMeasureHasBeenActivatedToEcfmpWebhook() { $measure = FlowMeasure::factory()->create(); - $discordNotification = DiscordNotification::factory()->create(); - $measure->discordNotifications()->attach( + $discordNotification = DivisionDiscordNotification::factory()->create(); + $measure->divisionDiscordNotifications()->attach( [ $discordNotification->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -119,10 +119,10 @@ public function testItShouldUseWebhookIfFlowMeasureHasNeverBeenActivatedOrNotifi public function testItShouldUseWebhookIfFlowMeasureHasOnlyBeenNotifiedToDivisionWebhookAsDifferentIdentifier() { $measure = FlowMeasure::factory()->create(); - $discordNotification = DiscordNotification::factory() + $discordNotification = DivisionDiscordNotification::factory() ->toDivisionWebhook($this->divisionDiscordWebhook) ->create(); - $measure->discordNotifications()->attach( + $measure->divisionDiscordNotifications()->attach( [ $discordNotification->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -144,10 +144,10 @@ public function testItShouldUseWebhookIfFlowMeasureHasOnlyBeenNotifiedToDivision public function testItShouldNotUseWebhookIfFlowMeasureHasBeenNotifiedToDivisionWebhookWithSameIdentifier() { $measure = FlowMeasure::factory()->create(); - $discordNotification = DiscordNotification::factory() + $discordNotification = DivisionDiscordNotification::factory() ->toDivisionWebhook($this->divisionDiscordWebhook) ->create(); - $measure->discordNotifications()->attach( + $measure->divisionDiscordNotifications()->attach( [ $discordNotification->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -169,10 +169,10 @@ public function testItShouldNotUseWebhookIfFlowMeasureHasBeenNotifiedToDivisionW public function testItShouldNotUseWebhookIfFlowMeasureHasBeenActivatedToDivisionWebhook() { $measure = FlowMeasure::factory()->create(); - $discordNotification = DiscordNotification::factory() + $discordNotification = DivisionDiscordNotification::factory() ->toDivisionWebhook($this->divisionDiscordWebhook) ->create(); - $measure->discordNotifications()->attach( + $measure->divisionDiscordNotifications()->attach( [ $discordNotification->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( diff --git a/tests/Discord/FlowMeasure/Webhook/Filter/WithdrawnWebhookFilterTest.php b/tests/Discord/FlowMeasure/Webhook/Filter/WithdrawnWebhookFilterTest.php index bf3f398f..bb8d5e4c 100644 --- a/tests/Discord/FlowMeasure/Webhook/Filter/WithdrawnWebhookFilterTest.php +++ b/tests/Discord/FlowMeasure/Webhook/Filter/WithdrawnWebhookFilterTest.php @@ -5,7 +5,7 @@ use App\Discord\FlowMeasure\Webhook\Filter\WithdrawnWebhookFilter; use App\Discord\Webhook\EcfmpWebhook; use App\Enums\DiscordNotificationType as DiscordNotificationTypeEnum; -use App\Models\DiscordNotification; +use App\Models\DivisionDiscordNotification; use App\Models\DiscordNotificationType; use App\Models\DivisionDiscordWebhook; use App\Models\FlowMeasure; @@ -28,8 +28,8 @@ public function setUp(): void public function testItShouldUseEcfmpWebhookIfItHasBeenNotified() { $measure = FlowMeasure::factory()->create(); - $discordNotification = DiscordNotification::factory()->create(); - $measure->discordNotifications()->attach( + $discordNotification = DivisionDiscordNotification::factory()->create(); + $measure->divisionDiscordNotifications()->attach( [ $discordNotification->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -50,8 +50,8 @@ public function testItShouldUseEcfmpWebhookIfItHasBeenNotified() public function testItShouldUseEcfmpWebhookIfItHasBeenActivated() { $measure = FlowMeasure::factory()->create(); - $discordNotification = DiscordNotification::factory()->create(); - $measure->discordNotifications()->attach( + $discordNotification = DivisionDiscordNotification::factory()->create(); + $measure->divisionDiscordNotifications()->attach( [ $discordNotification->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -72,8 +72,8 @@ public function testItShouldUseEcfmpWebhookIfItHasBeenActivated() public function testItShouldNotUseEcfmpWebhookIfItHasBeenWithdrawn() { $measure = FlowMeasure::factory()->create(); - $discordNotification = DiscordNotification::factory()->create(); - $measure->discordNotifications()->attach( + $discordNotification = DivisionDiscordNotification::factory()->create(); + $measure->divisionDiscordNotifications()->attach( [ $discordNotification->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -83,7 +83,7 @@ public function testItShouldNotUseEcfmpWebhookIfItHasBeenWithdrawn() ], ] ); - $measure->discordNotifications()->attach( + $measure->divisionDiscordNotifications()->attach( [ $discordNotification->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -105,8 +105,8 @@ public function testItShouldNotUseEcfmpWebhookIfItHasBeenWithdrawn() public function testItShouldNotUseEcfmpWebhookIfItHasBeenExpired() { $measure = FlowMeasure::factory()->create(); - $discordNotification = DiscordNotification::factory()->create(); - $measure->discordNotifications()->attach( + $discordNotification = DivisionDiscordNotification::factory()->create(); + $measure->divisionDiscordNotifications()->attach( [ $discordNotification->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -116,7 +116,7 @@ public function testItShouldNotUseEcfmpWebhookIfItHasBeenExpired() ], ] ); - $measure->discordNotifications()->attach( + $measure->divisionDiscordNotifications()->attach( [ $discordNotification->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -138,10 +138,10 @@ public function testItShouldNotUseEcfmpWebhookIfItHasBeenExpired() public function testItShouldUseDivisionWebhookIfItHasBeenNotified() { $measure = FlowMeasure::factory()->create(); - $discordNotification = DiscordNotification::factory() + $discordNotification = DivisionDiscordNotification::factory() ->toDivisionWebhook($this->divisionDiscordWebhook) ->create(); - $measure->discordNotifications()->attach( + $measure->divisionDiscordNotifications()->attach( [ $discordNotification->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -162,10 +162,10 @@ public function testItShouldUseDivisionWebhookIfItHasBeenNotified() public function testItShouldUseDivisionWebhookIfItHasBeenActivated() { $measure = FlowMeasure::factory()->create(); - $discordNotification = DiscordNotification::factory() + $discordNotification = DivisionDiscordNotification::factory() ->toDivisionWebhook($this->divisionDiscordWebhook) ->create(); - $measure->discordNotifications()->attach( + $measure->divisionDiscordNotifications()->attach( [ $discordNotification->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -186,10 +186,10 @@ public function testItShouldUseDivisionWebhookIfItHasBeenActivated() public function testItShouldNotUseDivisionWebhookIfItHasBeenWithdrawn() { $measure = FlowMeasure::factory()->create(); - $discordNotification = DiscordNotification::factory() + $discordNotification = DivisionDiscordNotification::factory() ->toDivisionWebhook($this->divisionDiscordWebhook) ->create(); - $measure->discordNotifications()->attach( + $measure->divisionDiscordNotifications()->attach( [ $discordNotification->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -199,7 +199,7 @@ public function testItShouldNotUseDivisionWebhookIfItHasBeenWithdrawn() ], ] ); - $measure->discordNotifications()->attach( + $measure->divisionDiscordNotifications()->attach( [ $discordNotification->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -221,10 +221,10 @@ public function testItShouldNotUseDivisionWebhookIfItHasBeenWithdrawn() public function testItShouldNotUseDivisionWebhookIfItHasBeenExpired() { $measure = FlowMeasure::factory()->create(); - $discordNotification = DiscordNotification::factory() + $discordNotification = DivisionDiscordNotification::factory() ->toDivisionWebhook($this->divisionDiscordWebhook) ->create(); - $measure->discordNotifications()->attach( + $measure->divisionDiscordNotifications()->attach( [ $discordNotification->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -234,7 +234,7 @@ public function testItShouldNotUseDivisionWebhookIfItHasBeenExpired() ], ] ); - $measure->discordNotifications()->attach( + $measure->divisionDiscordNotifications()->attach( [ $discordNotification->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( diff --git a/tests/Discord/Message/Embed/EmbedCollectionTest.php b/tests/Discord/Message/Embed/EmbedCollectionTest.php index 2e88441c..6f6a0811 100644 --- a/tests/Discord/Message/Embed/EmbedCollectionTest.php +++ b/tests/Discord/Message/Embed/EmbedCollectionTest.php @@ -3,6 +3,7 @@ namespace Tests\Discord\Message\Embed; use App\Discord\Message\Embed\AuthorInterface; +use App\Discord\Message\Embed\TitleInterface; use App\Discord\Message\Embed\Embed; use App\Discord\Message\Embed\EmbedCollection; use Mockery; @@ -34,4 +35,22 @@ public function testItMapsEmbeds() ->toArray() ); } + + public function testItMapsEmbedsToProtobuf() + { + $mockTitle1 = Mockery::mock(TitleInterface::class); + $mockTitle1->shouldReceive('title')->once()->andReturn('Foo'); + $mockTitle2 = Mockery::mock(TitleInterface::class); + $mockTitle2->shouldReceive('title')->once()->andReturn('Bar'); + + $embeds = (new EmbedCollection()) + ->add(Embed::make()->withTitle($mockTitle1)) + ->add(Embed::make()->withTitle($mockTitle2)) + ->toProtobuf(); + + $this->assertCount(2, $embeds); + + $this->assertEquals('Foo', $embeds[0]->getTitle()); + $this->assertEquals('Bar', $embeds[1]->getTitle()); + } } diff --git a/tests/Discord/Message/Embed/EmbedTest.php b/tests/Discord/Message/Embed/EmbedTest.php index 6cb120a5..16c9fdb5 100644 --- a/tests/Discord/Message/Embed/EmbedTest.php +++ b/tests/Discord/Message/Embed/EmbedTest.php @@ -149,4 +149,99 @@ public function testItHasAFooter() $this->assertEquals($expected, Embed::make()->withFooter($mockFooter)->toArray()); } + + public function testItHasATitleInProtobuf() + { + $mockTitle = Mockery::mock(TitleInterface::class); + $mockTitle->shouldReceive('title')->once()->andReturn('Foo'); + + + $this->assertEquals('Foo', Embed::make()->withTitle($mockTitle)->toProtobuf()->getTitle()); + } + + public function testItHasAColourInProtobuf() + { + $this->assertEquals( + Colour::ACTIVATED->value, + Embed::make()->withColour(Colour::ACTIVATED)->toProtobuf()->getColor() + ); + } + + + public function testItHasADescriptionInProtobuf() + { + $mockDescription = Mockery::mock(DescriptionInterface::class); + $mockDescription->shouldReceive('description')->once()->andReturn('Foo'); + + $this->assertEquals('Foo', Embed::make()->withDescription($mockDescription)->toProtobuf()->getDescription()); + } + + public function testItHasFieldsInProtobuf() + { + $field1 = Mockery::mock(FieldInterface::class); + $field1->shouldReceive('name')->once()->andReturn('Field1'); + $field1->shouldReceive('value')->once()->andReturn('Value1'); + $field1->shouldReceive('inline')->once()->andReturn(true); + $field2 = Mockery::mock(FieldInterface::class); + $field2->shouldReceive('name')->once()->andReturn('Field2'); + $field2->shouldReceive('value')->once()->andReturn('Value2'); + $field2->shouldReceive('inline')->once()->andReturn(false); + + $embed = Embed::make()->withField($field1)->withField($field2); + $embedProtobuf = $embed->toProtobuf(); + $embedField1 = $embedProtobuf->getFields()[0]; + + $this->assertEquals('Field1', $embedField1->getName()); + $this->assertEquals('Value1', $embedField1->getValue()); + $this->assertEquals(true, $embedField1->getInline()); + + $embedField2 = $embedProtobuf->getFields()[1]; + $this->assertEquals('Field2', $embedField2->getName()); + $this->assertEquals('Value2', $embedField2->getValue()); + $this->assertEquals(false, $embedField2->getInline()); + } + + public function testItSkipsFieldsByConditionInProtobuf() + { + $field1 = Mockery::mock(FieldInterface::class); + $field1->shouldReceive('name')->once()->andReturn('Field1'); + $field1->shouldReceive('value')->once()->andReturn('Value1'); + $field1->shouldReceive('inline')->once()->andReturn(true); + $field2 = Mockery::mock(FieldInterface::class); + + $embed = Embed::make()->withField($field1)->withField($field2, false); + $embedProtobuf = $embed->toProtobuf(); + $embedField1 = $embedProtobuf->getFields()[0]; + + $this->assertEquals('Field1', $embedField1->getName()); + $this->assertEquals('Value1', $embedField1->getValue()); + $this->assertEquals(true, $embedField1->getInline()); + + $this->assertCount(1, $embedProtobuf->getFields()); + } + + public function testItHasFieldsByCollectionInProtobuf() + { + $field1 = Mockery::mock(FieldInterface::class); + $field1->shouldReceive('name')->once()->andReturn('Field1'); + $field1->shouldReceive('value')->once()->andReturn('Value1'); + $field1->shouldReceive('inline')->once()->andReturn(true); + $field2 = Mockery::mock(FieldInterface::class); + $field2->shouldReceive('name')->once()->andReturn('Field2'); + $field2->shouldReceive('value')->once()->andReturn('Value2'); + $field2->shouldReceive('inline')->once()->andReturn(false); + + $embed = Embed::make()->withFields(collect([$field1, $field2])); + $embedProtobuf = $embed->toProtobuf(); + $embedField1 = $embedProtobuf->getFields()[0]; + + $this->assertEquals('Field1', $embedField1->getName()); + $this->assertEquals('Value1', $embedField1->getValue()); + $this->assertEquals(true, $embedField1->getInline()); + + $embedField2 = $embedProtobuf->getFields()[1]; + $this->assertEquals('Field2', $embedField2->getName()); + $this->assertEquals('Value2', $embedField2->getValue()); + $this->assertEquals(false, $embedField2->getInline()); + } } diff --git a/tests/Discord/Message/Sender/SenderTest.php b/tests/Discord/Message/Sender/DivisionWebhookSenderTest.php similarity index 86% rename from tests/Discord/Message/Sender/SenderTest.php rename to tests/Discord/Message/Sender/DivisionWebhookSenderTest.php index a04c0a8d..d8b8ddbc 100644 --- a/tests/Discord/Message/Sender/SenderTest.php +++ b/tests/Discord/Message/Sender/DivisionWebhookSenderTest.php @@ -2,18 +2,18 @@ namespace Tests\Discord\Message\Sender; -use App\Discord\DiscordInterface; +use App\Discord\DiscordWebhookInterface; use App\Discord\FlowMeasure\Message\MessageGeneratorInterface; use App\Discord\Message\Associator\AssociatorInterface; use App\Discord\Message\Embed\EmbedCollection; use App\Discord\Message\Logger\LoggerInterface; use App\Discord\Message\MessageInterface; -use App\Discord\Message\Sender\Sender; +use App\Discord\Message\Sender\DivisionWebhookSender; use App\Models\DivisionDiscordWebhook; use Mockery; use Tests\TestCase; -class SenderTest extends TestCase +class DivisionWebhookSenderTest extends TestCase { public function testItSendsMessages() { @@ -43,15 +43,15 @@ public function testItSendsMessages() $mockGenerator = Mockery::mock(MessageGeneratorInterface::class); $mockGenerator->expects('generate')->andReturn(collect([$message1, $message2])); - $mockDiscord = Mockery::mock(DiscordInterface::class); + $mockDiscord = Mockery::mock(DiscordWebhookInterface::class); $mockDiscord->shouldReceive('sendMessage')->with($message1)->andReturnTrue(); $mockDiscord->shouldReceive('sendMessage')->with($message2)->andReturnTrue(); - $sender = new Sender([$mockGenerator], $mockDiscord); + $sender = new DivisionWebhookSender([$mockGenerator], $mockDiscord); $sender->sendDiscordMessages(); $this->assertDatabaseHas( - 'discord_notifications', + 'division_discord_notifications', [ 'division_discord_webhook_id' => $divisionWebhook->id, 'content' => 'foo', @@ -59,7 +59,7 @@ public function testItSendsMessages() ); $this->assertDatabaseHas( - 'discord_notifications', + 'division_discord_notifications', [ 'division_discord_webhook_id' => $divisionWebhook->id, 'content' => 'bar', diff --git a/tests/Feature/Filament/DivisionDiscordWebhookResource/FlightInformationRegionRelationManagerTest.php b/tests/Feature/Filament/DivisionDiscordWebhookResource/FlightInformationRegionRelationManagerTest.php new file mode 100644 index 00000000..bd9bb36c --- /dev/null +++ b/tests/Feature/Filament/DivisionDiscordWebhookResource/FlightInformationRegionRelationManagerTest.php @@ -0,0 +1,98 @@ +actingAs(User::factory()->system()->create()); + + $webhook = DivisionDiscordWebhook::factory()->create(); + + livewire(FlightinformationRegionsRelationManager::class, [ + 'ownerRecord' => $webhook, + ]) + ->assertSuccessful(); +}); + +it('can save relation without tag', function () { + /** @var FrontendTestCase $this */ + $this->actingAs(User::factory()->system()->create()); + + $webhook = DivisionDiscordWebhook::factory()->create(); + $fir = FlightInformationRegion::factory()->create(); + + livewire(FlightinformationRegionsRelationManager::class, [ + 'ownerRecord' => $webhook, + ]) + ->callTableAction('attach-fir', data: [ + 'recordId' => $fir->id, + ]) + ->assertHasNoTableActionErrors(); + + $this->assertDatabaseHas( + 'division_discord_webhook_flight_information_region', + [ + 'division_discord_webhook_id' => $webhook->id, + 'flight_information_region_id' => $fir->id, + 'tag' => null, + ] + ); +}); + +it('can save relation with tag', function () { + /** @var FrontendTestCase $this */ + $this->actingAs(User::factory()->system()->create()); + + $webhook = DivisionDiscordWebhook::factory()->create(); + $fir = FlightInformationRegion::factory()->create(); + + livewire(FlightinformationRegionsRelationManager::class, [ + 'ownerRecord' => $webhook, + ]) + ->callTableAction('attach-fir', data: [ + 'recordId' => $fir->id, + 'tag' => 'abc' + ]) + ->assertHasNoTableActionErrors(); + + $this->assertDatabaseHas( + 'division_discord_webhook_flight_information_region', + [ + 'division_discord_webhook_id' => $webhook->id, + 'flight_information_region_id' => $fir->id, + 'tag' => 'abc', + ] + ); +}); + +it('doesnt save relation with long tag', function () { + /** @var FrontendTestCase $this */ + $this->actingAs(User::factory()->system()->create()); + + $webhook = DivisionDiscordWebhook::factory()->create(); + $fir = FlightInformationRegion::factory()->create(); + + livewire(FlightinformationRegionsRelationManager::class, [ + 'ownerRecord' => $webhook, + ]) + ->callTableAction('attach-fir', data: [ + 'recordId' => $fir->id, + 'tag' => Str::padRight('', 256, 'a') + ]) + ->assertHasTableActionErrors(['tag']); + + $this->assertDatabaseMissing( + 'division_discord_webhook_flight_information_region', + [ + 'division_discord_webhook_id' => $webhook->id, + 'flight_information_region_id' => $fir->id, + ] + ); +}); diff --git a/tests/Feature/Filament/DivisionDiscordWebhookResourceTest.php b/tests/Feature/Filament/DivisionDiscordWebhookResourceTest.php new file mode 100644 index 00000000..c46b0745 --- /dev/null +++ b/tests/Feature/Filament/DivisionDiscordWebhookResourceTest.php @@ -0,0 +1,322 @@ +get(DivisionDiscordWebhookResource::getUrl())->assertForbidden(); + + $this->actingAs(User::factory()->eventManager()->create()); + $this->get(DivisionDiscordWebhookResource::getUrl())->assertForbidden(); + + $this->actingAs(User::factory()->flowManager()->create()); + $this->get(DivisionDiscordWebhookResource::getUrl())->assertForbidden(); + + $this->actingAs(User::factory()->networkManager()->create()); + $this->get(DivisionDiscordWebhookResource::getUrl())->assertSuccessful(); + + $this->actingAs(User::factory()->system()->create()); + $this->get(DivisionDiscordWebhookResource::getUrl())->assertSuccessful(); +}); + +it('nmt and system have create action', function () { + $this->actingAs(User::factory()->networkManager()->create()); + livewire(ListDivisionDiscordWebhooks::class) + ->assertPageActionExists('create'); + + + $this->actingAs(User::factory()->system()->create()); + livewire(ListDivisionDiscordWebhooks::class) + ->assertPageActionExists('create'); +}); + +it('nmt and system can create', function () { + $this->get(DivisionDiscordWebhookResource::getUrl('create'))->assertForbidden(); + + $this->actingAs(User::factory()->eventManager()->create()); + $this->get(DivisionDiscordWebhookResource::getUrl('create'))->assertForbidden(); + + $this->actingAs(User::factory()->flowManager()->create()); + $this->get(DivisionDiscordWebhookResource::getUrl('create'))->assertForbidden(); + + $this->actingAs(User::factory()->networkManager()->create()); + $this->get(DivisionDiscordWebhookResource::getUrl('create'))->assertSuccessful(); + + $this->actingAs(User::factory()->system()->create()); + $this->get(DivisionDiscordWebhookResource::getUrl('create'))->assertSuccessful(); +}); + + +it('can be created by nmt', function () { + $this->actingAs(User::factory()->networkManager()->create()); + livewire(CreateDivisionDiscordWebhook::class) + ->set('data.description', 'test') + ->set('data.url', 'https://ecfmp.vatsim.net') + ->call('create') + ->assertHasNoErrors(); + + $this->assertDatabaseHas( + 'division_discord_webhooks', + [ + 'description' => 'test', + 'url' => 'https://ecfmp.vatsim.net' + ] + ); +}); + +it('can be created by system', function () { + $this->actingAs(User::factory()->system()->create()); + livewire(CreateDivisionDiscordWebhook::class) + ->set('data.description', 'test') + ->set('data.url', 'https://ecfmp.vatsim.net') + ->call('create') + ->assertHasNoErrors(); + + $this->assertDatabaseHas( + 'division_discord_webhooks', + [ + 'description' => 'test', + 'url' => 'https://ecfmp.vatsim.net' + ] + ); +}); + +it('rejects missing descriptions on create', function () { + $this->actingAs(User::factory()->networkManager()->create()); + livewire(CreateDivisionDiscordWebhook::class) + ->set('data.url', 'https://ecfmp.vatsim.net') + ->call('create') + ->assertHasErrors(['data.description']); + + $this->assertDatabaseCount( + 'division_discord_webhooks', + 0 + ); +}); + +it('rejects empty descriptions on create', function () { + $this->actingAs(User::factory()->networkManager()->create()); + livewire(CreateDivisionDiscordWebhook::class) + ->set('data.description', '') + ->set('data.url', 'https://ecfmp.vatsim.net') + ->call('create') + ->assertHasErrors(['data.description']); + + $this->assertDatabaseCount( + 'division_discord_webhooks', + 0 + ); +}); + +it('rejects large descriptions on create', function () { + $this->actingAs(User::factory()->networkManager()->create()); + livewire(CreateDivisionDiscordWebhook::class) + ->set('data.description', Str::padRight('', 256, 'a')) + ->set('data.url', 'https://ecfmp.vatsim.net') + ->call('create') + ->assertHasErrors(['data.description']); + + $this->assertDatabaseCount( + 'division_discord_webhooks', + 0 + ); +}); + +it('rejects missing urls on create', function () { + $this->actingAs(User::factory()->networkManager()->create()); + livewire(CreateDivisionDiscordWebhook::class) + ->set('data.description', 'abc') + ->call('create') + ->assertHasErrors(['data.url']); + + $this->assertDatabaseCount( + 'division_discord_webhooks', + 0 + ); +}); + +it('rejects empty urls on create', function () { + $this->actingAs(User::factory()->networkManager()->create()); + livewire(CreateDivisionDiscordWebhook::class) + ->set('data.description', 'abc') + ->set('data.url', '') + ->call('create') + ->assertHasErrors(['data.url']); + + $this->assertDatabaseCount( + 'division_discord_webhooks', + 0 + ); +}); + +it('rejects excessive urls on create', function () { + $this->actingAs(User::factory()->networkManager()->create()); + livewire(CreateDivisionDiscordWebhook::class) + ->set('data.description', 'abc') + ->set('data.url', Str::padRight('https://ecfmp.vatsim.net/', 501, 'a')) + ->call('create') + ->assertHasErrors(['data.url']); + + $this->assertDatabaseCount( + 'division_discord_webhooks', + 0 + ); +}); + +it('rejects invalid urls on create', function () { + $this->actingAs(User::factory()->networkManager()->create()); + livewire(CreateDivisionDiscordWebhook::class) + ->set('data.description', 'abc') + ->set('data.url', 'aaaaa') + ->call('create') + ->assertHasErrors(['data.url']); + + $this->assertDatabaseCount( + 'division_discord_webhooks', + 0 + ); +}); + +it('nmt and system can edit', function () { + $webhook = DivisionDiscordWebhook::factory()->create(); + + $this->get(DivisionDiscordWebhookResource::getUrl('edit', ['record' => $webhook->id]))->assertForbidden(); + + $this->actingAs(User::factory()->eventManager()->create()); + $this->get(DivisionDiscordWebhookResource::getUrl('edit', ['record' => $webhook->id]))->assertForbidden(); + + $this->actingAs(User::factory()->flowManager()->create()); + $this->get(DivisionDiscordWebhookResource::getUrl('edit', ['record' => $webhook->id]))->assertForbidden(); + + $this->actingAs(User::factory()->networkManager()->create()); + $this->get(DivisionDiscordWebhookResource::getUrl('edit', ['record' => $webhook->id]))->assertSuccessful(); + + $this->actingAs(User::factory()->system()->create()); + $this->get(DivisionDiscordWebhookResource::getUrl('edit', ['record' => $webhook->id]))->assertSuccessful(); +}); + +it('can be edited by nmt', function () { + $webhook = DivisionDiscordWebhook::factory()->create(); + + $this->actingAs(User::factory()->networkManager()->create()); + livewire(EditDivisionDiscordWebhook::class, ['record' => $webhook->id]) + ->set('data.description', 'test') + ->set('data.url', 'https://ecfmp.vatsim.net') + ->call('save') + ->assertHasNoErrors(); + + $this->assertDatabaseHas( + 'division_discord_webhooks', + [ + 'id' => $webhook->id, + 'description' => 'test', + 'url' => 'https://ecfmp.vatsim.net' + ] + ); +}); + +it('can be edited by system', function () { + $webhook = DivisionDiscordWebhook::factory()->create(); + + $this->actingAs(User::factory()->system()->create()); + livewire(EditDivisionDiscordWebhook::class, ['record' => $webhook->id]) + ->set('data.description', 'test') + ->set('data.url', 'https://ecfmp.vatsim.net') + ->call('save') + ->assertHasNoErrors(); + + $this->assertDatabaseHas( + 'division_discord_webhooks', + [ + 'id' => $webhook->id, + 'description' => 'test', + 'url' => 'https://ecfmp.vatsim.net' + ] + ); +}); + +it('rejects missing descriptions on edit', function () { + $webhook = DivisionDiscordWebhook::factory()->create(); + + $this->actingAs(User::factory()->networkManager()->create()); + livewire(EditDivisionDiscordWebhook::class, ['record' => $webhook->id]) + ->set('data.description', null) + ->set('data.url', 'https://ecfmp.vatsim.net') + ->call('save') + ->assertHasErrors(['data.description']); +}); + +it('rejects empty descriptions on edit', function () { + $webhook = DivisionDiscordWebhook::factory()->create(); + + $this->actingAs(User::factory()->networkManager()->create()); + livewire(EditDivisionDiscordWebhook::class, ['record' => $webhook->id]) + ->set('data.description', '') + ->set('data.url', 'https://ecfmp.vatsim.net') + ->call('save') + ->assertHasErrors(['data.description']); +}); + +it('rejects large descriptions on edit', function () { + $webhook = DivisionDiscordWebhook::factory()->create(); + + $this->actingAs(User::factory()->networkManager()->create()); + livewire(EditDivisionDiscordWebhook::class, ['record' => $webhook->id]) + ->set('data.description', Str::padRight('', 256, 'a')) + ->set('data.url', 'https://ecfmp.vatsim.net') + ->call('save') + ->assertHasErrors(['data.description']); +}); + +it('rejects missing urls on edit', function () { + $webhook = DivisionDiscordWebhook::factory()->create(); + + $this->actingAs(User::factory()->networkManager()->create()); + livewire(EditDivisionDiscordWebhook::class, ['record' => $webhook->id]) + ->set('data.description', 'abc') + ->set('data.url', null) + ->call('save') + ->assertHasErrors(['data.url']); +}); + +it('rejects empty urls on edit', function () { + $webhook = DivisionDiscordWebhook::factory()->create(); + + $this->actingAs(User::factory()->networkManager()->create()); + livewire(EditDivisionDiscordWebhook::class, ['record' => $webhook->id]) + ->set('data.description', 'abc') + ->set('data.url', '') + ->call('save') + ->assertHasErrors(['data.url']); +}); + +it('rejects excessive urls on edit', function () { + $webhook = DivisionDiscordWebhook::factory()->create(); + + $this->actingAs(User::factory()->networkManager()->create()); + livewire(EditDivisionDiscordWebhook::class, ['record' => $webhook->id]) + ->set('data.description', 'abc') + ->set('data.url', Str::padRight('https://ecfmp.vatsim.net/', 501, 'a')) + ->call('save') + ->assertHasErrors(['data.url']); +}); + +it('rejects invalid urls on edit', function () { + $webhook = DivisionDiscordWebhook::factory()->create(); + + $this->actingAs(User::factory()->networkManager()->create()); + livewire(EditDivisionDiscordWebhook::class, ['record' => $webhook->id]) + ->set('data.description', 'abc') + ->set('data.url', 'aaaaa') + ->call('save') + ->assertHasErrors(['data.url']); +}); diff --git a/tests/Feature/Filament/FlowMeasureResourceTest.php b/tests/Feature/Filament/FlowMeasureResourceTest.php index 0285efe9..ebee9913 100644 --- a/tests/Feature/Filament/FlowMeasureResourceTest.php +++ b/tests/Feature/Filament/FlowMeasureResourceTest.php @@ -2,9 +2,11 @@ use App\Enums\FlowMeasureType; use App\Filament\Resources\FlowMeasureResource; +use App\Helpers\FlowMeasureIdentifierGenerator; use App\Models\FlowMeasure; use App\Models\FlightInformationRegion; use App\Models\User; +use Carbon\Carbon; use Illuminate\Support\Arr; use Tests\FrontendTestCase; @@ -81,7 +83,11 @@ ->set("data.ades.{$adesKey}.custom_value", $newData->filters[1]['value'][0]) ->call('create'); + $latestFlowMeasure = FlowMeasure::latest()->first(); $this->assertDatabaseHas(FlowMeasure::class, [ + 'identifier' => $latestFlowMeasure->identifier, + 'canonical_identifier' => FlowMeasureIdentifierGenerator::canonicalIdentifier($latestFlowMeasure->identifier), + 'revision_number' => FlowMeasureIdentifierGenerator::timesRevised($latestFlowMeasure->identifier), 'flight_information_region_id' => $newData->flight_information_region_id, 'event_id' => $newData->event_id, 'start_time' => $newData->start_time->startOfMinute(), @@ -290,7 +296,9 @@ $this->actingAs(User::factory()->system()->create()); /** @var FlowMeasure $flowMeasure */ - $flowMeasure = FlowMeasure::factory()->create(); + $flowMeasure = FlowMeasure::factory()->create(['start_time' => Carbon::now()->addMinutes(35)]); + $flowMeasure->notifiedFlightInformationRegions()->attach(FlightInformationRegion::factory()->create()); + $nextIdentifier = FlowMeasureIdentifierGenerator::generateIdentifier($flowMeasure->start_time, $flowMeasure->flightInformationRegion); /** @var FlowMeasure $newData */ $newData = FlowMeasure::factory()->make(); @@ -298,6 +306,7 @@ $livewire = livewire(FlowMeasureResource\Pages\EditFlowMeasure::class, [ 'record' => $flowMeasure->getKey(), ]) + ->set('data.flight_information_region_id', $newData->flight_information_region_id) ->set('data.reason', $newData->reason) ->set('data.type', $newData->type->value) ->set('data.value', $newData->value) @@ -310,7 +319,57 @@ $adepKey = key($adep); $adesKey = key($ades); - $livewire->set("data.adep.{$adepKey}.value_type", 'custom_value') + $test = $livewire->set("data.adep.{$adepKey}.value_type", 'custom_value') + ->set("data.adep.{$adepKey}.airport_group", null) + ->set("data.adep.{$adepKey}.custom_value", $newData->filters[0]['value'][0]) + ->set("data.ades.{$adesKey}.value_type", 'custom_value') + ->set("data.ades.{$adesKey}.airport_group", null) + ->set("data.ades.{$adesKey}.custom_value", $newData->filters[1]['value'][0]) + ->call('save'); + + $newFlowMeasure = FlowMeasure::where('identifier', $nextIdentifier)->first(); + + expect($newFlowMeasure)->toMatchArray([ + // TODO Re-enable this after I know why it doesn't update in test, but does in front-end + // 'reason' => $newData->reason, + 'identifier' => $nextIdentifier, + 'canonical_identifier' => $nextIdentifier, + 'revision_number' => 0, + 'type' => $newData->type, + 'value' => $newData->value, + 'mandatory_route' => $newData->mandatory_route, + ]); +}); + +it('can edit with revision', function () { + /** @var FrontendTestCase $this */ + $this->actingAs(User::factory()->system()->create()); + + /** @var FlowMeasure $flowMeasure */ + $flowMeasure = FlowMeasure::factory()->create(['start_time' => Carbon::now()->addMinutes(15)]); + $flowMeasure->notifiedFlightInformationRegions()->attach(FlightInformationRegion::factory()->create()); + $originalIdentifier = $flowMeasure->identifier; + $nextIdentifier = FlowMeasureIdentifierGenerator::generateRevisedIdentifier($flowMeasure); + /** @var FlowMeasure $newData */ + $newData = FlowMeasure::factory()->make(); + + $livewire = livewire(FlowMeasureResource\Pages\EditFlowMeasure::class, [ + 'record' => $flowMeasure->getKey(), + ]) + ->set('data.flight_information_region_id', $newData->flight_information_region_id) + ->set('data.reason', $newData->reason) + ->set('data.type', $newData->type->value) + ->set('data.value', $newData->value) + ->set('data.mandatory_route', $newData->mandatory_route); + + /** @var array $data */ + $data = $livewire->get('data'); + $adep = Arr::get($data, 'adep'); + $ades = Arr::get($data, 'ades'); + $adepKey = key($adep); + $adesKey = key($ades); + + $test = $livewire->set("data.adep.{$adepKey}.value_type", 'custom_value') ->set("data.adep.{$adepKey}.airport_group", null) ->set("data.adep.{$adepKey}.custom_value", $newData->filters[0]['value'][0]) ->set("data.ades.{$adesKey}.value_type", 'custom_value') @@ -321,6 +380,9 @@ expect($flowMeasure->refresh())->toMatchArray([ // TODO Re-enable this after I know why it doesn't update in test, but does in front-end // 'reason' => $newData->reason, + 'identifier' => $nextIdentifier, + 'canonical_identifier' => $originalIdentifier, + 'revision_number' => 1, 'type' => $newData->type, 'value' => $newData->value, 'mandatory_route' => $newData->mandatory_route, diff --git a/tests/Helpers/FlowMeasureIdentifierGeneratorTest.php b/tests/Helpers/FlowMeasureIdentifierGeneratorTest.php index c4e15ffb..7a1942e4 100644 --- a/tests/Helpers/FlowMeasureIdentifierGeneratorTest.php +++ b/tests/Helpers/FlowMeasureIdentifierGeneratorTest.php @@ -7,6 +7,7 @@ use App\Models\FlowMeasure; use Carbon\Carbon; use Illuminate\Support\Facades\DB; +use PHPUnit\Framework\Attributes\DataProvider; use Tests\TestCase; class FlowMeasureIdentifierGeneratorTest extends TestCase @@ -156,4 +157,26 @@ public function revisionIdentifierProvider(): array 'Tenth revision' => ['EGTT01A-11', 10], ]; } + + #[DataProvider('canonicalIdentifierProvider')] + public function testItGeneratesCanonicalIdentifiers( + string $identifier, + string $expected + ) { + $this->assertEquals( + $expected, + FlowMeasureIdentifierGenerator::canonicalIdentifier( + FlowMeasure::factory()->make(['identifier' => $identifier]) + ) + ); + } + + public function canonicalIdentifierProvider(): array + { + return [ + 'No revision' => ['EGTT01A', 'EGTT01A'], + 'First revision' => ['EGTT31B-2', 'EGTT31BA'], + 'Tenth revision' => ['EGTT05A-11', 'EGTT05A'], + ]; + } } diff --git a/tests/Jobs/SendDiscordNotificationsTest.php b/tests/Jobs/SendDiscordNotificationsTest.php index d1473fde..1cf9f1d2 100644 --- a/tests/Jobs/SendDiscordNotificationsTest.php +++ b/tests/Jobs/SendDiscordNotificationsTest.php @@ -2,7 +2,8 @@ namespace Tests\Jobs; -use App\Discord\Message\Sender\Sender; +use App\Discord\FlowMeasure\Generator\EcfmpFlowMeasureMessageGenerator; +use App\Discord\Message\Sender\DivisionWebhookSender; use App\Jobs\SendDiscordNotifications; use Illuminate\Support\Facades\Config; use Mockery; @@ -14,9 +15,13 @@ public function testItRunsNotificationSending() { Config::set('discord.enabled', true); - $senderMock = Mockery::mock(Sender::class); + $senderMock = Mockery::mock(DivisionWebhookSender::class); $senderMock->shouldReceive('sendDiscordMessages')->once(); - $this->app->instance(Sender::class, $senderMock); + $this->app->instance(DivisionWebhookSender::class, $senderMock); + + $serviceSenderMock = Mockery::mock(EcfmpFlowMeasureMessageGenerator::class); + $serviceSenderMock->shouldReceive('generateAndSend')->once(); + $this->app->instance(EcfmpFlowMeasureMessageGenerator::class, $serviceSenderMock); $job = $this->app->make(SendDiscordNotifications::class); $job->handle(); @@ -26,9 +31,13 @@ public function testItDoesntRunsNotificationSendingIfSwitchedOff() { Config::set('discord.enabled', false); - $senderMock = Mockery::mock(Sender::class); + $senderMock = Mockery::mock(DivisionWebhookSender::class); $senderMock->shouldReceive('sendDiscordMessages')->never(); - $this->app->instance(Sender::class, $senderMock); + $this->app->instance(DivisionWebhookSender::class, $senderMock); + + $serviceSenderMock = Mockery::mock(EcfmpFlowMeasureMessageGenerator::class); + $serviceSenderMock->shouldReceive('generateAndSend')->never(); + $this->app->instance(EcfmpFlowMeasureMessageGenerator::class, $serviceSenderMock); $job = $this->app->make(SendDiscordNotifications::class); $job->handle(); diff --git a/tests/Repository/FlowMeasureNotification/ActiveRepositoryTest.php b/tests/Repository/FlowMeasureNotification/ActiveRepositoryTest.php index b15a8ed8..cb840b34 100644 --- a/tests/Repository/FlowMeasureNotification/ActiveRepositoryTest.php +++ b/tests/Repository/FlowMeasureNotification/ActiveRepositoryTest.php @@ -2,9 +2,11 @@ namespace Tests\Repository\FlowMeasureNotification; -use App\Enums\DiscordNotificationType; +use App\Enums\DiscordNotificationType as DiscordNotificationTypeEnum; +use App\Models\DiscordNotificationType; use App\Models\FlowMeasure; use App\Repository\FlowMeasureNotification\ActiveRepository; +use Illuminate\Support\Str; use Tests\TestCase; class ActiveRepositoryTest extends TestCase @@ -20,7 +22,7 @@ public function setUp(): void public function testItHasANotificationType() { $this->assertEquals( - DiscordNotificationType::FLOW_MEASURE_ACTIVATED, + DiscordNotificationTypeEnum::FLOW_MEASURE_ACTIVATED, $this->repository->notificationType() ); } @@ -59,4 +61,149 @@ public function testItIgnoresExpiredFlowMeasures() $this->assertEmpty($this->repository->flowMeasuresForNotification()); } + + public function testItReturnsEcfmpMeasuresToSend() + { + // Should send, is active and never notified + [$measure1, $measure2] = FlowMeasure::factory()->count(2)->create(); + + // Active, but should send, has been notified with a different identifier + $measure3 = FlowMeasure::factory() + ->afterCreating( + function (FlowMeasure $measure) { + $measure->discordNotifications()->create( + [ + 'remote_id' => Str::uuid(), + ], + joining: [ + 'discord_notification_type_id' => DiscordNotificationType::idFromEnum(DiscordNotificationTypeEnum::FLOW_MEASURE_ACTIVATED), + 'notified_as' => 'different_identifier', + ] + ); + } + ) + ->create(); + + // Active, but should send, has been notified as a notified type + $measure4 = FlowMeasure::factory() + ->afterCreating( + function (FlowMeasure $measure) { + $measure->discordNotifications()->create( + [ + 'remote_id' => Str::uuid(), + ], + joining: [ + 'discord_notification_type_id' => DiscordNotificationType::idFromEnum(DiscordNotificationTypeEnum::FLOW_MEASURE_NOTIFIED), + 'notified_as' => 'different_identifier', + ] + ); + } + ) + ->create(); + + // Should not send, is only notified + FlowMeasure::factory()->notStarted()->count(1)->create(); + + // Should not send, is expired + FlowMeasure::factory()->finished()->count(1)->create(); + + // Should not send, is withdrawn + FlowMeasure::factory()->withdrawn()->count(1)->create(); + + // Active, but should not send, has already been notified with this identifier + FlowMeasure::factory() + ->afterCreating( + function (FlowMeasure $measure) { + $measure->discordNotifications()->create( + [ + 'remote_id' => Str::uuid(), + ], + joining: [ + 'discord_notification_type_id' => DiscordNotificationType::idFromEnum(DiscordNotificationTypeEnum::FLOW_MEASURE_ACTIVATED), + 'notified_as' => $measure->identifier, + ] + ); + } + ) + ->create(); + + $this->assertEquals( + [$measure1->id, $measure2->id, $measure3->id, $measure4->id], + $this->repository->flowMeasuresToBeSentToEcfmp()->pluck('measure.id')->toArray() + ); + } + + public function testItIsNotReissueIfNeverPreviouslyActivated() + { + // Should send, is active and never notified + $measure = FlowMeasure::factory()->create(); + $measuresToNotify = $this->repository->flowMeasuresToBeSentToEcfmp(); + + $this->assertCount(1, $measuresToNotify); + $this->assertEquals($measure->id, $measuresToNotify->first()->measure->id); + $this->assertFalse($measuresToNotify->first()->isReissuedNotification); + } + + public function testItIsNotReissueIfPreviousVersionWasNotifiedForTheSameIdentifier() + { + // Should send, is active and never notified + $measure = FlowMeasure::factory()->create(); + $measure->discordNotifications()->create( + [ + 'remote_id' => Str::uuid(), + ], + joining: [ + 'discord_notification_type_id' => DiscordNotificationType::idFromEnum(DiscordNotificationTypeEnum::FLOW_MEASURE_NOTIFIED), + 'notified_as' => $measure->identifier, + ] + ); + + $measuresToNotify = $this->repository->flowMeasuresToBeSentToEcfmp(); + + $this->assertCount(1, $measuresToNotify); + $this->assertEquals($measure->id, $measuresToNotify->first()->measure->id); + $this->assertFalse($measuresToNotify->first()->isReissuedNotification); + } + + public function testItIsReissuedIfPreviousWasNotifiedUnderDifferentIdentifier() + { + // Should send, is active and never notified + $measure = FlowMeasure::factory()->create(); + $measure->discordNotifications()->create( + [ + 'remote_id' => Str::uuid(), + ], + joining: [ + 'discord_notification_type_id' => DiscordNotificationType::idFromEnum(DiscordNotificationTypeEnum::FLOW_MEASURE_NOTIFIED), + 'notified_as' => 'something_else', + ] + ); + + $measuresToNotify = $this->repository->flowMeasuresToBeSentToEcfmp(); + + $this->assertCount(1, $measuresToNotify); + $this->assertEquals($measure->id, $measuresToNotify->first()->measure->id); + $this->assertTrue($measuresToNotify->first()->isReissuedNotification); + } + + public function testItIsReissuedIfPreviousWasActivatedUnderDifferentIdentifier() + { + // Should send, is active and never notified + $measure = FlowMeasure::factory()->create(); + $measure->discordNotifications()->create( + [ + 'remote_id' => Str::uuid(), + ], + joining: [ + 'discord_notification_type_id' => DiscordNotificationType::idFromEnum(DiscordNotificationTypeEnum::FLOW_MEASURE_ACTIVATED), + 'notified_as' => 'something_else', + ] + ); + + $measuresToNotify = $this->repository->flowMeasuresToBeSentToEcfmp(); + + $this->assertCount(1, $measuresToNotify); + $this->assertEquals($measure->id, $measuresToNotify->first()->measure->id); + $this->assertTrue($measuresToNotify->first()->isReissuedNotification); + } } diff --git a/tests/Repository/FlowMeasureNotification/ExpiredRepositoryTest.php b/tests/Repository/FlowMeasureNotification/ExpiredRepositoryTest.php index ba56990e..4ce7ecec 100644 --- a/tests/Repository/FlowMeasureNotification/ExpiredRepositoryTest.php +++ b/tests/Repository/FlowMeasureNotification/ExpiredRepositoryTest.php @@ -2,9 +2,13 @@ namespace Tests\Repository\FlowMeasureNotification; -use App\Enums\DiscordNotificationType; +use App\Enums\DiscordNotificationType as DiscordNotificationTypeEnum; +use App\Models\DiscordNotification; +use App\Models\DiscordNotificationType; use App\Models\FlowMeasure; use App\Repository\FlowMeasureNotification\ExpiredRepository; +use Carbon\Carbon; +use Illuminate\Support\Str; use Tests\TestCase; class ExpiredRepositoryTest extends TestCase @@ -14,13 +18,13 @@ class ExpiredRepositoryTest extends TestCase public function setUp(): void { parent::setUp(); - $this->repository = new ExpiredRepository(); + $this->repository = $this->app->make(ExpiredRepository::class); } public function testItHasANotificationType() { $this->assertEquals( - DiscordNotificationType::FLOW_MEASURE_EXPIRED, + DiscordNotificationTypeEnum::FLOW_MEASURE_EXPIRED, $this->repository->notificationType() ); } @@ -63,4 +67,204 @@ public function testItIgnoresActiveFlowMeasures() FlowMeasure::factory()->count(3)->create(); $this->assertEmpty($this->repository->flowMeasuresForNotification()); } + + public function testItReturnsEmptyForEcfmpMeasuresIfMoreThanFiveNotificationsSentInLastTwoHours() + { + DiscordNotification::factory()->count(6)->create(['created_at' => Carbon::now()->subHours(2)->addMinute()]); + + // Would be sent to ECFMP if not for the webhook limit + FlowMeasure::factory()->finishedRecently()->count(3)->create(); + + $this->assertEmpty($this->repository->flowMeasuresToBeSentToEcfmp()); + } + + public function testItReturnsEcfmpMeasures() + { + // Will be sent, notified as notified previously + $flowMeasure1 = FlowMeasure::factory()->finishedRecently()->afterCreating( + function (FlowMeasure $measure) { + $notification = $measure->discordNotifications()->create( + [ + 'remote_id' => Str::uuid(), + ], + joining: [ + 'discord_notification_type_id' => DiscordNotificationType::idFromEnum(DiscordNotificationTypeEnum::FLOW_MEASURE_NOTIFIED), + 'notified_as' => $measure->identifier, + ] + ); + $notification->created_at = Carbon::now()->subHours(3); + $notification->save(); + } + )->create(); + + // Will be sent, notified as activated previously + $flowMeasure2 = FlowMeasure::factory()->finishedRecently()->afterCreating( + function (FlowMeasure $measure) { + $notification = $measure->discordNotifications()->create( + [ + 'remote_id' => Str::uuid(), + ], + joining: [ + 'discord_notification_type_id' => DiscordNotificationType::idFromEnum(DiscordNotificationTypeEnum::FLOW_MEASURE_ACTIVATED), + 'notified_as' => $measure->identifier, + ] + ); + $notification->created_at = Carbon::now()->subHours(3); + $notification->save(); + } + )->create(); + + // Wont be sent, high revision number + FlowMeasure::factory()->finishedRecently()->afterCreating( + function (FlowMeasure $measure) { + $measure->revision_number = 2; + $measure->save(); + } + )->create(); + + // Wont be sent, never previously notified + FlowMeasure::factory()->finishedRecently()->create(); + + // Wont be sent, notified as expired previously + FlowMeasure::factory()->finishedRecently()->afterCreating( + function (FlowMeasure $measure) { + $notification1 = $measure->discordNotifications()->create( + [ + 'created_at' => Carbon::now()->subHours(3), + 'remote_id' => Str::uuid(), + ], + joining: [ + 'discord_notification_type_id' => DiscordNotificationType::idFromEnum(DiscordNotificationTypeEnum::FLOW_MEASURE_ACTIVATED), + 'notified_as' => $measure->identifier, + ] + ); + $notification1->created_at = Carbon::now()->subHours(3); + $notification1->save(); + + $notification2 = $measure->discordNotifications()->create( + [ + 'created_at' => Carbon::now()->subHours(3), + 'remote_id' => Str::uuid(), + ], + joining: [ + 'discord_notification_type_id' => DiscordNotificationType::idFromEnum(DiscordNotificationTypeEnum::FLOW_MEASURE_EXPIRED), + 'notified_as' => $measure->identifier, + ] + ); + $notification2->created_at = Carbon::now()->subHours(3); + $notification2->save(); + } + )->create(); + + // Won't be sent, notified as withdrawn previously + FlowMeasure::factory()->finishedRecently()->afterCreating( + function (FlowMeasure $measure) { + $notification1 = $measure->discordNotifications()->create( + [ + 'created_at' => Carbon::now()->subHours(3), + 'remote_id' => Str::uuid(), + ], + joining: [ + 'discord_notification_type_id' => DiscordNotificationType::idFromEnum(DiscordNotificationTypeEnum::FLOW_MEASURE_ACTIVATED), + 'notified_as' => $measure->identifier, + ] + ); + $notification1->created_at = Carbon::now()->subHours(3); + $notification1->save(); + + $notification2 = $measure->discordNotifications()->create( + [ + 'created_at' => Carbon::now()->subHours(3), + 'remote_id' => Str::uuid(), + ], + joining: [ + 'discord_notification_type_id' => DiscordNotificationType::idFromEnum(DiscordNotificationTypeEnum::FLOW_MEASURE_WITHDRAWN), + 'notified_as' => $measure->identifier, + ] + ); + + $notification2->created_at = Carbon::now()->subHours(3); + $notification2->save(); + } + )->create(); + + // Won't be sent, notification sent as notified but still notified + FlowMeasure::factory()->notified()->afterCreating( + function (FlowMeasure $measure) { + $notification = $measure->discordNotifications()->create( + [ + 'created_at' => Carbon::now()->subHours(3), + 'remote_id' => Str::uuid(), + ], + joining: [ + 'discord_notification_type_id' => DiscordNotificationType::idFromEnum(DiscordNotificationTypeEnum::FLOW_MEASURE_NOTIFIED), + 'notified_as' => $measure->identifier, + ] + ); + + $notification->created_at = Carbon::now()->subHours(3); + $notification->save(); + } + )->create(); + + // Won't be sent, notification sent as active but still active + FlowMeasure::factory()->afterCreating( + function (FlowMeasure $measure) { + $measure->discordNotifications()->create( + [ + 'created_at' => Carbon::now()->subHours(3), + 'remote_id' => Str::uuid(), + ], + joining: [ + 'discord_notification_type_id' => DiscordNotificationType::idFromEnum(DiscordNotificationTypeEnum::FLOW_MEASURE_ACTIVATED), + 'notified_as' => $measure->identifier, + ] + ); + } + )->create(); + + // Won't be sent, notified as active but expired a long time ago + FlowMeasure::factory()->finishedAWhileAgo()->afterCreating( + function (FlowMeasure $measure) { + $measure->discordNotifications()->create( + [ + 'created_at' => Carbon::now()->subHours(3), + 'remote_id' => Str::uuid(), + ], + joining: [ + 'discord_notification_type_id' => DiscordNotificationType::idFromEnum(DiscordNotificationTypeEnum::FLOW_MEASURE_ACTIVATED), + 'notified_as' => $measure->identifier, + ] + ); + } + )->create(); + + $this->assertEquals( + [$flowMeasure1->id, $flowMeasure2->id], + $this->repository->flowMeasuresToBeSentToEcfmp()->pluck('measure.id')->toArray() + ); + } + + public function testItIsNotReissued() + { + // Will be sent, notified as notified previously + $measure = FlowMeasure::factory()->finishedRecently()->afterCreating( + function (FlowMeasure $measure) { + $notification = $measure->discordNotifications()->create( + [ + 'remote_id' => Str::uuid(), + ], + joining: [ + 'discord_notification_type_id' => DiscordNotificationType::idFromEnum(DiscordNotificationTypeEnum::FLOW_MEASURE_NOTIFIED), + 'notified_as' => $measure->identifier, + ] + ); + $notification->created_at = Carbon::now()->subHours(3); + $notification->save(); + } + )->create(); + + $this->assertEquals($measure->id, $this->repository->flowMeasuresToBeSentToEcfmp()->first()->measure->id); + $this->assertFalse($this->repository->flowMeasuresToBeSentToEcfmp()->first()->isReissuedNotification); + } } diff --git a/tests/Repository/FlowMeasureNotification/NotifiedRepositoryTest.php b/tests/Repository/FlowMeasureNotification/NotifiedRepositoryTest.php index 35255f91..8b03500b 100644 --- a/tests/Repository/FlowMeasureNotification/NotifiedRepositoryTest.php +++ b/tests/Repository/FlowMeasureNotification/NotifiedRepositoryTest.php @@ -2,9 +2,11 @@ namespace Tests\Repository\FlowMeasureNotification; -use App\Enums\DiscordNotificationType; +use App\Enums\DiscordNotificationType as DiscordNotificationTypeEnum; +use App\Models\DiscordNotificationType; use App\Models\FlowMeasure; use App\Repository\FlowMeasureNotification\NotifiedRepository; +use Illuminate\Support\Str; use Tests\TestCase; class NotifiedRepositoryTest extends TestCase @@ -20,7 +22,7 @@ public function setUp(): void public function testItHasANotificationType() { $this->assertEquals( - DiscordNotificationType::FLOW_MEASURE_NOTIFIED, + DiscordNotificationTypeEnum::FLOW_MEASURE_NOTIFIED, $this->repository->notificationType() ); } @@ -57,4 +59,105 @@ function (FlowMeasure $measure) { $this->assertEmpty($this->repository->flowMeasuresForNotification()); } + + public function testItReturnsMeasuresToBeSentToEcfmp() + { + // Should be sent, never notified + [$measure1, $measure2] = FlowMeasure::factory()->notified()->count(2)->create(); + + // Should be sent, previously notified under a different identifier + $measure3 = FlowMeasure::factory()->notified()->afterCreating( + function (FlowMeasure $measure) { + $measure->discordNotifications()->create( + [ + 'remote_id' => Str::uuid(), + ], + joining: [ + 'discord_notification_type_id' => DiscordNotificationType::idFromEnum(DiscordNotificationTypeEnum::FLOW_MEASURE_NOTIFIED), + 'notified_as' => 'different_identifier', + ] + ); + } + ) + ->create(); + + // Should not be sent, has been previously activated + FlowMeasure::factory()->afterCreating( + function (FlowMeasure $measure) { + $measure->discordNotifications()->create( + [ + 'remote_id' => Str::uuid(), + ], + joining: [ + 'discord_notification_type_id' => DiscordNotificationType::idFromEnum(DiscordNotificationTypeEnum::FLOW_MEASURE_ACTIVATED), + 'notified_as' => $measure->identifier, + ] + ); + } + ); + + // Should not be sent, already notified with this identifier + FlowMeasure::factory()->afterCreating( + function (FlowMeasure $measure) { + $measure->discordNotifications()->create( + [ + 'remote_id' => Str::uuid(), + ], + joining: [ + 'discord_notification_type_id' => DiscordNotificationType::idFromEnum(DiscordNotificationTypeEnum::FLOW_MEASURE_NOTIFIED), + 'notified_as' => $measure->identifier, + ] + ); + } + ); + + // Should not be sent, already active + FlowMeasure::factory()->count(3)->create(); + + // Should not be sent, not yet in notified range + FlowMeasure::factory()->notNotified()->count(3)->create(); + + // Should not be sent, expired + FlowMeasure::factory()->finished()->count(3)->create(); + + // Should not be sent, withdrawn + FlowMeasure::factory()->notified()->withdrawn()->count(3)->create(); + + $this->assertEquals( + [$measure1->id, $measure2->id, $measure3->id], + $this->repository->flowMeasuresToBeSentToEcfmp()->pluck('measure.id')->toArray() + ); + } + + public function testItIsNotReissueIfNeverPreviouslyNotified() + { + // Should send, is active and never notified + $measure = FlowMeasure::factory()->notified()->create(); + $measuresToNotify = $this->repository->flowMeasuresToBeSentToEcfmp(); + + $this->assertCount(1, $measuresToNotify); + $this->assertEquals($measure->id, $measuresToNotify->first()->measure->id); + $this->assertFalse($measuresToNotify->first()->isReissuedNotification); + } + + public function testItIsReissuedIfPreviousWasNotifiedUnderDifferentIdentifier() + { + // Should send, is active and never notified + $measure = FlowMeasure::factory()->notified()->create(); + $measure->discordNotifications()->create( + [ + 'remote_id' => Str::uuid(), + ], + joining: [ + 'discord_notification_type_id' => DiscordNotificationType::idFromEnum(DiscordNotificationTypeEnum::FLOW_MEASURE_NOTIFIED), + 'notified_as' => 'something_else', + ] + ); + + $measuresToNotify = $this->repository->flowMeasuresToBeSentToEcfmp(); + + $this->assertCount(1, $measuresToNotify); + $this->assertEquals($measure->id, $measuresToNotify->first()->measure->id); + $this->assertTrue($measuresToNotify->first()->isReissuedNotification); + } } diff --git a/tests/Repository/FlowMeasureNotification/WithdrawnRepositoryTest.php b/tests/Repository/FlowMeasureNotification/WithdrawnRepositoryTest.php index 81311b61..eaaae252 100644 --- a/tests/Repository/FlowMeasureNotification/WithdrawnRepositoryTest.php +++ b/tests/Repository/FlowMeasureNotification/WithdrawnRepositoryTest.php @@ -2,10 +2,13 @@ namespace Tests\Repository\FlowMeasureNotification; -use App\Enums\DiscordNotificationType; +use App\Enums\DiscordNotificationType as DiscordNotificationTypeEnum; +use App\Models\DiscordNotificationType; use App\Models\FlowMeasure; use App\Repository\FlowMeasureNotification\WithdrawnRepository; use Carbon\Carbon; +use DB; +use Illuminate\Support\Str; use Tests\TestCase; class WithdrawnRepositoryTest extends TestCase @@ -21,7 +24,7 @@ public function setUp(): void public function testItHasANotificationType() { $this->assertEquals( - DiscordNotificationType::FLOW_MEASURE_WITHDRAWN, + DiscordNotificationTypeEnum::FLOW_MEASURE_WITHDRAWN, $this->repository->notificationType() ); } @@ -114,4 +117,145 @@ public function testItIgnoresDeletedExpiredFlowMeasures() $this->assertEmpty($this->repository->flowMeasuresForNotification()); } + + public function testItReturnsFlowMeasuresToSendToEcfmp() + { + // Should be sent, was notified and is now withdrawn + $measure1 = FlowMeasure::factory() + ->withdrawn() + ->afterCreating(function (FlowMeasure $measure) { + $measure->discordNotifications()->create( + [ + 'remote_id' => Str::uuid(), + ], + joining: [ + 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( + DiscordNotificationTypeEnum::FLOW_MEASURE_NOTIFIED + ), + 'notified_as' => $measure->identifier + ] + ); + }) + ->create(); + + // Should be sent, was active and is now withdrawn + $measure2 = FlowMeasure::factory() + ->withdrawn() + ->afterCreating(function (FlowMeasure $measure) { + $measure->discordNotifications()->create( + [ + 'remote_id' => Str::uuid(), + ], + joining: [ + 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( + DiscordNotificationTypeEnum::FLOW_MEASURE_ACTIVATED + ), + 'notified_as' => $measure->identifier + ] + ); + }) + ->create(); + + // Should not be sent, is active + FlowMeasure::factory() + ->afterCreating(function (FlowMeasure $measure) { + $measure->discordNotifications()->create( + [ + 'remote_id' => Str::uuid(), + ], + joining: [ + 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( + DiscordNotificationTypeEnum::FLOW_MEASURE_ACTIVATED + ), + 'notified_as' => $measure->identifier + ] + ); + }) + ->create(); + + // Should not be sent, is notified + FlowMeasure::factory() + ->afterCreating(function (FlowMeasure $measure) { + $measure->discordNotifications()->create( + [ + 'remote_id' => Str::uuid(), + ], + joining: [ + 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( + DiscordNotificationTypeEnum::FLOW_MEASURE_NOTIFIED + ), + 'notified_as' => $measure->identifier + ] + ); + }) + ->notified() + ->create(); + + // Should not be sent, is widthdrawn but already notified as withdrawn with some identifier + FlowMeasure::factory() + ->withdrawn() + ->afterCreating(function (FlowMeasure $measure) { + $measure->discordNotifications()->create( + [ + 'remote_id' => Str::uuid(), + ], + joining: [ + 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( + DiscordNotificationTypeEnum::FLOW_MEASURE_WITHDRAWN + ), + 'notified_as' => 'an_identifier' + ] + ); + }) + ->create(); + + // Should not be sent, is widthdrawn but already notified as expired with some identifier + FlowMeasure::factory() + ->withdrawn() + ->afterCreating(function (FlowMeasure $measure) { + $measure->discordNotifications()->create( + [ + 'remote_id' => Str::uuid(), + ], + joining: [ + 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( + DiscordNotificationTypeEnum::FLOW_MEASURE_EXPIRED + ), + 'notified_as' => 'an_identifier' + ] + ); + }) + ->create(); + // DB::commit(); + // dd('hi'); + + $this->assertEquals( + [$measure1->id, $measure2->id], + $this->repository->flowMeasuresToBeSentToEcfmp()->pluck('measure.id')->toArray() + ); + } + + public function testItIsNotReissued() + { + // Should be sent, was notified and is now withdrawn + $measure = FlowMeasure::factory() + ->withdrawn() + ->afterCreating(function (FlowMeasure $measure) { + $measure->discordNotifications()->create( + [ + 'remote_id' => Str::uuid(), + ], + joining: [ + 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( + DiscordNotificationTypeEnum::FLOW_MEASURE_NOTIFIED + ), + 'notified_as' => $measure->identifier + ] + ); + }) + ->create(); + + $this->assertEquals($measure->id, $this->repository->flowMeasuresToBeSentToEcfmp()->first()->measure->id); + $this->assertFalse($this->repository->flowMeasuresToBeSentToEcfmp()->first()->isReissuedNotification); + } } diff --git a/tests/TestCase.php b/tests/TestCase.php index a7db7d36..fe1f8016 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -14,6 +14,7 @@ abstract class TestCase extends BaseTestCase public function beforeRefreshingDatabase() { DB::table('discord_notifications')->delete(); + DB::table('division_discord_notifications')->delete(); DB::table('division_discord_webhooks')->delete(); DB::table('flow_measures')->delete(); DB::table('events')->delete();