From 076837e7d7e9b7d691aabd5c55d0a5b0b3c3a9b7 Mon Sep 17 00:00:00 2001 From: Andy Ford Date: Fri, 6 Oct 2023 15:10:35 +0100 Subject: [PATCH 01/34] build: docker compose healthcheck --- docker-compose.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 6c70c28d..1301cf7f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -43,8 +43,10 @@ services: healthcheck: test: [ "CMD", "mysqladmin", "ping", "-p${DB_PASSWORD}" ] interval: 10s - retries: 5 - timeout: 5s + retries: 10 + timeout: 100s + start_period: 15s + start_interval: 1s networks: sail: driver: bridge From e2ed48ccc40aa02bcacad59a8da86064512dec28 Mon Sep 17 00:00:00 2001 From: Andy Ford Date: Fri, 6 Oct 2023 15:11:53 +0100 Subject: [PATCH 02/34] deps: protobuf --- .github/workflows/test.yml | 2 ++ .gitmodules | 3 +++ protobuf | 1 + 3 files changed, 6 insertions(+) create mode 100644 .gitmodules create mode 160000 protobuf diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 97c3abbc..3b8c5faf 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -22,6 +22,8 @@ jobs: steps: - name: Checkout Code uses: actions/checkout@v2 + with: + submodules: true - name: Configure PHP uses: shivammathur/setup-php@v2 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/protobuf b/protobuf new file mode 160000 index 00000000..a8656fe1 --- /dev/null +++ b/protobuf @@ -0,0 +1 @@ +Subproject commit a8656fe1133492e6e72b6fa2020d9028ab12ce94 From fac1af8f34e611d9dc49cf59fc4056e9162699e0 Mon Sep 17 00:00:00 2001 From: Andy Ford Date: Sun, 8 Oct 2023 13:32:32 +0100 Subject: [PATCH 03/34] dev: schema dump --- database/schema/mysql-schema.sql | 726 +++++++++++++++++++++++++++++++ 1 file changed, 726 insertions(+) create mode 100644 database/schema/mysql-schema.sql diff --git a/database/schema/mysql-schema.sql b/database/schema/mysql-schema.sql new file mode 100644 index 00000000..7cc0c666 --- /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 `division_discord_notification_flow_measure` +-- + +DROP TABLE IF EXISTS `division_discord_notification_flow_measure`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `division_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 `division_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 `division_discord_notification_flow_measure` +-- + +LOCK TABLES `division_discord_notification_flow_measure` WRITE; +/*!40000 ALTER TABLE `division_discord_notification_flow_measure` DISABLE KEYS */; +/*!40000 ALTER TABLE `division_discord_notification_flow_measure` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `division_discord_notifications` +-- + +DROP TABLE IF EXISTS `division_discord_notifications`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `division_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 `division_discord_notifications` +-- + +LOCK TABLES `division_discord_notifications` WRITE; +/*!40000 ALTER TABLE `division_discord_notifications` DISABLE KEYS */; +/*!40000 ALTER TABLE `division_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),(42,'2023_10_08_112953_rename_discord_notifications_tables',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 From f603bff2794a0ed39ffbccaf2cbef7330dd66a7f Mon Sep 17 00:00:00 2001 From: Andy Ford Date: Sun, 8 Oct 2023 13:34:48 +0100 Subject: [PATCH 04/34] refactor: rename table to disambiguate purpose --- .../Associator/FlowMeasureAssociator.php | 4 +- .../Content/FlowMeasureRecipientsFactory.php | 18 +- .../Helper/NotificationReissuer.php | 14 +- .../FlowMeasure/Logger/FlowMeasureLogger.php | 4 +- .../Webhook/Filter/ActivatedWebhookFilter.php | 2 +- .../Webhook/Filter/ExpiredWebhookFilter.php | 12 +- .../Webhook/Filter/NotifiedWebhookFilter.php | 2 +- .../Associator/AssociatorInterface.php | 4 +- .../Message/Logger/LoggerInterface.php | 4 +- ...on.php => DivisionDiscordNotification.php} | 2 +- app/Models/FlowMeasure.php | 9 +- ...=> DivisionDiscordNotificationFactory.php} | 2 +- ...53_rename_discord_notifications_tables.php | 23 ++ ...scord_notification_flow_measures_table.php | 25 ++ .../Associator/FlowMeasureAssociatorTest.php | 10 +- .../Helper/NotificationReissuerTest.php | 224 ++++++++++-------- .../Logger/FlowMeasureLoggerTest.php | 10 +- .../Filter/ActivatedWebhookFilterTest.php | 14 +- .../Filter/ExpiredWebhookFilterTest.php | 24 +- .../Filter/NotifiedWebhookFilterTest.php | 14 +- .../Filter/WithdrawnWebhookFilterTest.php | 18 +- tests/TestCase.php | 2 +- 22 files changed, 261 insertions(+), 180 deletions(-) rename app/Models/{DiscordNotification.php => DivisionDiscordNotification.php} (96%) rename database/factories/{DiscordNotificationFactory.php => DivisionDiscordNotificationFactory.php} (93%) create mode 100644 database/migrations/2023_10_08_112953_rename_discord_notifications_tables.php create mode 100644 database/migrations/2023_10_08_122743_rename_column_on_division_discord_notification_flow_measures_table.php diff --git a/app/Discord/FlowMeasure/Associator/FlowMeasureAssociator.php b/app/Discord/FlowMeasure/Associator/FlowMeasureAssociator.php index c753b06f..9885b5b2 100644 --- a/app/Discord/FlowMeasure/Associator/FlowMeasureAssociator.php +++ b/app/Discord/FlowMeasure/Associator/FlowMeasureAssociator.php @@ -4,7 +4,7 @@ use App\Discord\Message\Associator\AssociatorInterface; use App\Enums\DiscordNotificationType as DiscordNotificationTypeEnum; -use App\Models\DiscordNotification; +use App\Models\DivisionDiscordNotification; use App\Models\DiscordNotificationType; use App\Models\FlowMeasure; @@ -20,7 +20,7 @@ public function __construct(FlowMeasure $flowMeasure, DiscordNotificationTypeEnu } - public function associate(DiscordNotification $notification): void + public function associate(DivisionDiscordNotification $notification): void { $this->flowMeasure->discordNotifications()->attach( [ diff --git a/app/Discord/FlowMeasure/Content/FlowMeasureRecipientsFactory.php b/app/Discord/FlowMeasure/Content/FlowMeasureRecipientsFactory.php index a7daecb1..5daddc28 100644 --- a/app/Discord/FlowMeasure/Content/FlowMeasureRecipientsFactory.php +++ b/app/Discord/FlowMeasure/Content/FlowMeasureRecipientsFactory.php @@ -5,7 +5,7 @@ use App\Discord\FlowMeasure\Provider\PendingMessageInterface; 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; @@ -29,18 +29,18 @@ private function hasRecentlyBeenNotified(PendingMessageInterface $pendingMessage $measure = $pendingMessage->flowMeasure(); return $pendingMessage->type( ) === DiscordNotificationType::FLOW_MEASURE_ACTIVATED && $measure->notifiedDiscordNotifications->firstWhere( - fn (DiscordNotification $notification) => $notification->created_at > Carbon::now()->subHour() && + fn(DivisionDiscordNotification $notification) => $notification->created_at > Carbon::now()->subHour() && $notification->pivot->notified_as === $measure->identifier - ) !== null; + ) !== null; } private function ecfmpRecipients(PendingMessageInterface $pendingMessage): FlowMeasureRecipientsInterface { return new EcfmpInterestedParties( $pendingMessage->flowMeasure()->notifiedFlightInformationRegions - ->map(fn (FlightInformationRegion $flightInformationRegion) => $flightInformationRegion->discordTags) + ->map(fn(FlightInformationRegion $flightInformationRegion) => $flightInformationRegion->discordTags) ->flatten() - ->map(fn (DiscordTag $discordTag) => new Tag($discordTag)) + ->map(fn(DiscordTag $discordTag) => new Tag($discordTag)) ); } @@ -49,13 +49,13 @@ private function divisionRecipients(PendingMessageInterface $pendingMessage): Fl $recipients = DivisionDiscordWebhook::find($pendingMessage->webhook()->id()) ->flightInformationRegions ->filter( - fn (FlightInformationRegion $flightInformationRegion) => $pendingMessage + fn(FlightInformationRegion $flightInformationRegion) => $pendingMessage ->flowMeasure() ->notifiedFlightInformationRegions - ->firstWhere(fn (FlightInformationRegion $notifiedFir) => $notifiedFir->id === $flightInformationRegion->id) + ->firstWhere(fn(FlightInformationRegion $notifiedFir) => $notifiedFir->id === $flightInformationRegion->id) ) - ->filter(fn (FlightInformationRegion $flightInformationRegion) => !empty($flightInformationRegion->pivot->tag)) - ->map(fn (FlightInformationRegion $flightInformationRegion) => new Tag($flightInformationRegion->pivot)); + ->filter(fn(FlightInformationRegion $flightInformationRegion) => !empty($flightInformationRegion->pivot->tag)) + ->map(fn(FlightInformationRegion $flightInformationRegion) => new Tag($flightInformationRegion->pivot)); return $recipients->isEmpty() ? new NoRecipients() diff --git a/app/Discord/FlowMeasure/Helper/NotificationReissuer.php b/app/Discord/FlowMeasure/Helper/NotificationReissuer.php index 025386c0..9a7b2e26 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 @@ -34,14 +34,14 @@ public function isReissuedNotification(): bool ->get(); return $notificationsOfType->filter( - fn ( - DiscordNotification $notification - ) => $notification->pivot->notified_as !== $this->measure->identifier + fn( + DivisionDiscordNotification $notification + ) => $notification->pivot->notified_as !== $this->measure->identifier )->isNotEmpty() && $notificationsOfType->filter( - fn ( - DiscordNotification $notification + fn( + DivisionDiscordNotification $notification ) => $notification->pivot->notified_as === $this->measure->identifier - )->isEmpty(); + )->isEmpty(); } public function measure(): FlowMeasure 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/Webhook/Filter/ActivatedWebhookFilter.php b/app/Discord/FlowMeasure/Webhook/Filter/ActivatedWebhookFilter.php index dbb21bdf..fff54eb3 100644 --- a/app/Discord/FlowMeasure/Webhook/Filter/ActivatedWebhookFilter.php +++ b/app/Discord/FlowMeasure/Webhook/Filter/ActivatedWebhookFilter.php @@ -13,7 +13,7 @@ public function shouldUseWebhook(FlowMeasure $flowMeasure, WebhookInterface $web { return $this->existingNotificationDoesntExist( $flowMeasure->activatedDiscordNotifications() - ->where('discord_notification_flow_measure.notified_as', $flowMeasure->identifier), + ->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..121b842f 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; @@ -44,9 +44,9 @@ public function shouldUseWebhook(FlowMeasure $flowMeasure, WebhookInterface $web 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..f2cb24d5 100644 --- a/app/Discord/FlowMeasure/Webhook/Filter/NotifiedWebhookFilter.php +++ b/app/Discord/FlowMeasure/Webhook/Filter/NotifiedWebhookFilter.php @@ -21,7 +21,7 @@ private function notYetNotified(FlowMeasure $flowMeasure, WebhookInterface $webh { return $this->existingNotificationDoesntExist( $flowMeasure->notifiedDiscordNotifications() - ->where('discord_notification_flow_measure.notified_as', $flowMeasure->identifier), + ->where('division_discord_notification_flow_measure.notified_as', $flowMeasure->identifier), $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/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/Models/DiscordNotification.php b/app/Models/DivisionDiscordNotification.php similarity index 96% rename from app/Models/DiscordNotification.php rename to app/Models/DivisionDiscordNotification.php index ac1bb7d9..acc231c7 100644 --- a/app/Models/DiscordNotification.php +++ b/app/Models/DivisionDiscordNotification.php @@ -9,7 +9,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\BelongsToMany; -class DiscordNotification extends Model +class DivisionDiscordNotification extends Model { use HasFactory; diff --git a/app/Models/FlowMeasure.php b/app/Models/FlowMeasure.php index 04ba2676..d2dbcf7e 100644 --- a/app/Models/FlowMeasure.php +++ b/app/Models/FlowMeasure.php @@ -118,7 +118,7 @@ public function scopeFlightInformationRegion( public function discordNotifications(): BelongsToMany { - return $this->belongsToMany(DiscordNotification::class) + return $this->belongsToMany(DivisionDiscordNotification::class) ->withPivot(['discord_notification_type_id', 'notified_as']) ->withTimestamps(); } @@ -180,7 +180,7 @@ public function filtersByType(FilterType $filterType): array return array_values( array_filter( $this->filters, - fn (array $filter) => FilterType::tryFrom($filter['type']) === $filterType + fn(array $filter) => FilterType::tryFrom($filter['type']) === $filterType ) ); } @@ -194,7 +194,7 @@ public function extraFilters(): array { return array_filter( $this->filters, - fn (array $filter) => !in_array( + fn(array $filter) => !in_array( FilterType::tryFrom($filter['type']), [FilterType::DEPARTURE_AIRPORTS, FilterType::ARRIVAL_AIRPORTS] ) @@ -215,7 +215,8 @@ public function scopeEndTimeWithinOneDay(Builder $builder): Builder public function status(): Attribute { - return new Attribute(function () { + return new Attribute(function () + { if ($this->trashed()) { return FlowMeasureStatus::DELETED; } diff --git a/database/factories/DiscordNotificationFactory.php b/database/factories/DivisionDiscordNotificationFactory.php similarity index 93% rename from database/factories/DiscordNotificationFactory.php rename to database/factories/DivisionDiscordNotificationFactory.php index 708a73ad..58dc4a2b 100644 --- a/database/factories/DiscordNotificationFactory.php +++ b/database/factories/DivisionDiscordNotificationFactory.php @@ -8,7 +8,7 @@ /** * @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\DiscordTag> */ -class DiscordNotificationFactory extends Factory +class DivisionDiscordNotificationFactory extends Factory { /** * Define the model's default state. 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..8db87ff0 --- /dev/null +++ b/database/migrations/2023_10_08_112953_rename_discord_notifications_tables.php @@ -0,0 +1,23 @@ +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/Helper/NotificationReissuerTest.php b/tests/Discord/FlowMeasure/Helper/NotificationReissuerTest.php index 7efdb8f3..ed3aae7b 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,17 +39,19 @@ 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(); + $previousNotification = DivisionDiscordNotification::factory()->create(); $this->flowMeasure->discordNotifications()->sync( [ $previousNotification->id => [ @@ -60,17 +64,19 @@ 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(); + $previousNotification = DivisionDiscordNotification::factory()->create(); $this->flowMeasure->discordNotifications()->sync( [ $previousNotification->id => [ @@ -83,17 +89,19 @@ 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(); + $previousNotification = DivisionDiscordNotification::factory()->create(); $this->flowMeasure->discordNotifications()->sync( [ $previousNotification->id => [ @@ -106,21 +114,23 @@ 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(); @@ -142,23 +152,25 @@ 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(); @@ -180,23 +192,25 @@ 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(); @@ -218,17 +232,19 @@ 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( @@ -243,17 +259,19 @@ 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(); + $previousNotification = DivisionDiscordNotification::factory()->create(); $this->flowMeasure->discordNotifications()->sync( [ $previousNotification->id => [ @@ -266,17 +284,19 @@ 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(); + $previousNotification = DivisionDiscordNotification::factory()->create(); $this->flowMeasure->discordNotifications()->sync( [ $previousNotification->id => [ @@ -289,17 +309,19 @@ 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(); + $previousNotification = DivisionDiscordNotification::factory()->create(); $this->flowMeasure->discordNotifications()->sync( [ $previousNotification->id => [ @@ -312,39 +334,45 @@ 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(); + $previousNotification = DivisionDiscordNotification::factory()->create(); $this->flowMeasure->discordNotifications()->sync( [ $previousNotification->id => [ @@ -357,17 +385,19 @@ 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(); + $previousNotification = DivisionDiscordNotification::factory()->create(); $this->flowMeasure->discordNotifications()->sync( [ $previousNotification->id => [ @@ -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/Webhook/Filter/ActivatedWebhookFilterTest.php b/tests/Discord/FlowMeasure/Webhook/Filter/ActivatedWebhookFilterTest.php index d194150f..4fff3c7c 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,7 +39,7 @@ public function testItShouldUseWebhookIfFlowMeasureHasNeverBeenActivatedToEcfmpW public function testItShouldUseWebhookIfFlowMeasureHasOnlyBeenNotifiedToEcfmpWebhook() { $measure = FlowMeasure::factory()->create(); - $discordNotification = DiscordNotification::factory()->create(); + $discordNotification = DivisionDiscordNotification::factory()->create(); $measure->discordNotifications()->attach( [ $discordNotification->id => [ @@ -62,7 +62,7 @@ public function testItShouldUseWebhookIfFlowMeasureHasOnlyBeenNotifiedToEcfmpWeb public function testItShouldUseWebhookIfFlowMeasureHasBeenActivatedAsDifferentIdentifierToEcfmpWebhook() { $measure = FlowMeasure::factory()->create(); - $discordNotification = DiscordNotification::factory()->create(); + $discordNotification = DivisionDiscordNotification::factory()->create(); $measure->discordNotifications()->attach( [ $discordNotification->id => [ @@ -85,7 +85,7 @@ public function testItShouldUseWebhookIfFlowMeasureHasBeenActivatedAsDifferentId public function testItShouldNotUseWebhookIfFlowMeasureHasBeenActivatedToEcfmpWebhook() { $measure = FlowMeasure::factory()->create(); - $discordNotification = DiscordNotification::factory()->create(); + $discordNotification = DivisionDiscordNotification::factory()->create(); $measure->discordNotifications()->attach( [ $discordNotification->id => [ @@ -119,7 +119,7 @@ public function testItShouldUseWebhookIfFlowMeasureHasNeverBeenActivatedToDivisi public function testItShouldUseWebhookIfFlowMeasureHasOnlyBeenNotifiedToDivisionWebhook() { $measure = FlowMeasure::factory()->create(); - $discordNotification = DiscordNotification::factory() + $discordNotification = DivisionDiscordNotification::factory() ->toDivisionWebhook($this->divisionDiscordWebhook) ->create(); $measure->discordNotifications()->attach( @@ -144,7 +144,7 @@ public function testItShouldUseWebhookIfFlowMeasureHasOnlyBeenNotifiedToDivision public function testItShouldUseWebhookIfFlowMeasureHasBeenActivatedAsDifferentIdentifierToDivisionWebhook() { $measure = FlowMeasure::factory()->create(); - $discordNotification = DiscordNotification::factory() + $discordNotification = DivisionDiscordNotification::factory() ->toDivisionWebhook($this->divisionDiscordWebhook) ->create(); $measure->discordNotifications()->attach( @@ -169,7 +169,7 @@ public function testItShouldUseWebhookIfFlowMeasureHasBeenActivatedAsDifferentId public function testItShouldNotUseWebhookIfFlowMeasureHasBeenActivatedToDivisionWebhook() { $measure = FlowMeasure::factory()->create(); - $discordNotification = DiscordNotification::factory() + $discordNotification = DivisionDiscordNotification::factory() ->toDivisionWebhook($this->divisionDiscordWebhook) ->create(); $measure->discordNotifications()->attach( diff --git a/tests/Discord/FlowMeasure/Webhook/Filter/ExpiredWebhookFilterTest.php b/tests/Discord/FlowMeasure/Webhook/Filter/ExpiredWebhookFilterTest.php index cad33a6d..0c6de4f9 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,7 +71,7 @@ public function testItShouldUseEcfmpWebhookIfThereAreOtherMeasuresActiveAroundTh public function testItShouldUseWebhookIfFlowMeasureHasOnlyBeenNotifiedToEcfmpWebhook() { $measure = FlowMeasure::factory()->create(['identifier' => 'EGTT23A-3']); - $discordNotification = DiscordNotification::factory()->create(); + $discordNotification = DivisionDiscordNotification::factory()->create(); $measure->discordNotifications()->attach( [ $discordNotification->id => [ @@ -94,7 +94,7 @@ public function testItShouldUseWebhookIfFlowMeasureHasOnlyBeenNotifiedToEcfmpWeb public function testItShouldUseWebhookIfFlowMeasureHasOnlyBeenActivatedToEcfmpWebhook() { $measure = FlowMeasure::factory()->create(['identifier' => 'EGTT23A-3']); - $discordNotification = DiscordNotification::factory()->create(); + $discordNotification = DivisionDiscordNotification::factory()->create(); $measure->discordNotifications()->attach( [ $discordNotification->id => [ @@ -117,7 +117,7 @@ public function testItShouldUseWebhookIfFlowMeasureHasOnlyBeenActivatedToEcfmpWe public function testItShouldNotUseWebhookIfFlowMeasureHasBeenWithdrawnToEcfmpWebhook() { $measure = FlowMeasure::factory()->create(['identifier' => 'EGTT23A-3']); - $discordNotification = DiscordNotification::factory()->create(); + $discordNotification = DivisionDiscordNotification::factory()->create(); $measure->discordNotifications()->attach( [ $discordNotification->id => [ @@ -140,7 +140,7 @@ public function testItShouldNotUseWebhookIfFlowMeasureHasBeenWithdrawnToEcfmpWeb public function testItShouldNotUseWebhookIfFlowMeasureHasBeenExpiredToEcfmpWebhook() { $measure = FlowMeasure::factory()->create(['identifier' => 'EGTT23A-3']); - $discordNotification = DiscordNotification::factory()->create(); + $discordNotification = DivisionDiscordNotification::factory()->create(); $measure->discordNotifications()->attach( [ $discordNotification->id => [ @@ -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,7 +188,7 @@ public function testItShouldNotUseWebhookIfDoesntMeetConditionsForEcfmpWebhook() public function testItShouldNotUseWebhookIfFlowMeasureHasOnlyBeenNotifiedToDivisionWebhook() { $measure = FlowMeasure::factory()->create(); - $discordNotification = DiscordNotification::factory() + $discordNotification = DivisionDiscordNotification::factory() ->toDivisionWebhook($this->divisionDiscordWebhook) ->create(); $measure->discordNotifications()->attach( @@ -213,7 +213,7 @@ public function testItShouldNotUseWebhookIfFlowMeasureHasOnlyBeenNotifiedToDivis public function testItShouldNotUseWebhookIfFlowMeasureHasOnlyBeenActivatedToDivisionWebhook() { $measure = FlowMeasure::factory()->create(); - $discordNotification = DiscordNotification::factory() + $discordNotification = DivisionDiscordNotification::factory() ->toDivisionWebhook($this->divisionDiscordWebhook) ->create(); $measure->discordNotifications()->attach( @@ -238,7 +238,7 @@ public function testItShouldNotUseWebhookIfFlowMeasureHasOnlyBeenActivatedToDivi public function testItShouldNotUseWebhookIfFlowMeasureHasBeenExpiredToDivisionWebhook() { $measure = FlowMeasure::factory()->create(); - $discordNotification = DiscordNotification::factory() + $discordNotification = DivisionDiscordNotification::factory() ->toDivisionWebhook($this->divisionDiscordWebhook) ->create(); $measure->discordNotifications()->attach( @@ -263,7 +263,7 @@ public function testItShouldNotUseWebhookIfFlowMeasureHasBeenExpiredToDivisionWe public function testItShouldNotUseWebhookIfFlowMeasureHasBeenWithdrawnToDivisionWebhook() { $measure = FlowMeasure::factory()->create(); - $discordNotification = DiscordNotification::factory() + $discordNotification = DivisionDiscordNotification::factory() ->toDivisionWebhook($this->divisionDiscordWebhook) ->create(); $measure->discordNotifications()->attach( diff --git a/tests/Discord/FlowMeasure/Webhook/Filter/NotifiedWebhookFilterTest.php b/tests/Discord/FlowMeasure/Webhook/Filter/NotifiedWebhookFilterTest.php index 0469021d..f507f6d2 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,7 +39,7 @@ public function testItShouldUseWebhookIfFlowMeasureHasNeverBeenActivatedOrNotifi public function testItShouldUseWebhookIfFlowMeasureHasBeenNotifiedToEcfmpWebhookUnderDifferentIdentifier() { $measure = FlowMeasure::factory()->create(); - $discordNotification = DiscordNotification::factory()->create(); + $discordNotification = DivisionDiscordNotification::factory()->create(); $measure->discordNotifications()->attach( [ $discordNotification->id => [ @@ -62,7 +62,7 @@ public function testItShouldUseWebhookIfFlowMeasureHasBeenNotifiedToEcfmpWebhook public function testItShouldNotUseWebhookIfFlowMeasureHasBeenNotifiedToEcfmpWebhook() { $measure = FlowMeasure::factory()->create(); - $discordNotification = DiscordNotification::factory()->create(); + $discordNotification = DivisionDiscordNotification::factory()->create(); $measure->discordNotifications()->attach( [ $discordNotification->id => [ @@ -85,7 +85,7 @@ public function testItShouldNotUseWebhookIfFlowMeasureHasBeenNotifiedToEcfmpWebh public function testItShouldNotUseWebhookIfFlowMeasureHasBeenActivatedToEcfmpWebhook() { $measure = FlowMeasure::factory()->create(); - $discordNotification = DiscordNotification::factory()->create(); + $discordNotification = DivisionDiscordNotification::factory()->create(); $measure->discordNotifications()->attach( [ $discordNotification->id => [ @@ -119,7 +119,7 @@ public function testItShouldUseWebhookIfFlowMeasureHasNeverBeenActivatedOrNotifi public function testItShouldUseWebhookIfFlowMeasureHasOnlyBeenNotifiedToDivisionWebhookAsDifferentIdentifier() { $measure = FlowMeasure::factory()->create(); - $discordNotification = DiscordNotification::factory() + $discordNotification = DivisionDiscordNotification::factory() ->toDivisionWebhook($this->divisionDiscordWebhook) ->create(); $measure->discordNotifications()->attach( @@ -144,7 +144,7 @@ public function testItShouldUseWebhookIfFlowMeasureHasOnlyBeenNotifiedToDivision public function testItShouldNotUseWebhookIfFlowMeasureHasBeenNotifiedToDivisionWebhookWithSameIdentifier() { $measure = FlowMeasure::factory()->create(); - $discordNotification = DiscordNotification::factory() + $discordNotification = DivisionDiscordNotification::factory() ->toDivisionWebhook($this->divisionDiscordWebhook) ->create(); $measure->discordNotifications()->attach( @@ -169,7 +169,7 @@ public function testItShouldNotUseWebhookIfFlowMeasureHasBeenNotifiedToDivisionW public function testItShouldNotUseWebhookIfFlowMeasureHasBeenActivatedToDivisionWebhook() { $measure = FlowMeasure::factory()->create(); - $discordNotification = DiscordNotification::factory() + $discordNotification = DivisionDiscordNotification::factory() ->toDivisionWebhook($this->divisionDiscordWebhook) ->create(); $measure->discordNotifications()->attach( diff --git a/tests/Discord/FlowMeasure/Webhook/Filter/WithdrawnWebhookFilterTest.php b/tests/Discord/FlowMeasure/Webhook/Filter/WithdrawnWebhookFilterTest.php index bf3f398f..e95ca3a2 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,7 +28,7 @@ public function setUp(): void public function testItShouldUseEcfmpWebhookIfItHasBeenNotified() { $measure = FlowMeasure::factory()->create(); - $discordNotification = DiscordNotification::factory()->create(); + $discordNotification = DivisionDiscordNotification::factory()->create(); $measure->discordNotifications()->attach( [ $discordNotification->id => [ @@ -50,7 +50,7 @@ public function testItShouldUseEcfmpWebhookIfItHasBeenNotified() public function testItShouldUseEcfmpWebhookIfItHasBeenActivated() { $measure = FlowMeasure::factory()->create(); - $discordNotification = DiscordNotification::factory()->create(); + $discordNotification = DivisionDiscordNotification::factory()->create(); $measure->discordNotifications()->attach( [ $discordNotification->id => [ @@ -72,7 +72,7 @@ public function testItShouldUseEcfmpWebhookIfItHasBeenActivated() public function testItShouldNotUseEcfmpWebhookIfItHasBeenWithdrawn() { $measure = FlowMeasure::factory()->create(); - $discordNotification = DiscordNotification::factory()->create(); + $discordNotification = DivisionDiscordNotification::factory()->create(); $measure->discordNotifications()->attach( [ $discordNotification->id => [ @@ -105,7 +105,7 @@ public function testItShouldNotUseEcfmpWebhookIfItHasBeenWithdrawn() public function testItShouldNotUseEcfmpWebhookIfItHasBeenExpired() { $measure = FlowMeasure::factory()->create(); - $discordNotification = DiscordNotification::factory()->create(); + $discordNotification = DivisionDiscordNotification::factory()->create(); $measure->discordNotifications()->attach( [ $discordNotification->id => [ @@ -138,7 +138,7 @@ public function testItShouldNotUseEcfmpWebhookIfItHasBeenExpired() public function testItShouldUseDivisionWebhookIfItHasBeenNotified() { $measure = FlowMeasure::factory()->create(); - $discordNotification = DiscordNotification::factory() + $discordNotification = DivisionDiscordNotification::factory() ->toDivisionWebhook($this->divisionDiscordWebhook) ->create(); $measure->discordNotifications()->attach( @@ -162,7 +162,7 @@ public function testItShouldUseDivisionWebhookIfItHasBeenNotified() public function testItShouldUseDivisionWebhookIfItHasBeenActivated() { $measure = FlowMeasure::factory()->create(); - $discordNotification = DiscordNotification::factory() + $discordNotification = DivisionDiscordNotification::factory() ->toDivisionWebhook($this->divisionDiscordWebhook) ->create(); $measure->discordNotifications()->attach( @@ -186,7 +186,7 @@ public function testItShouldUseDivisionWebhookIfItHasBeenActivated() public function testItShouldNotUseDivisionWebhookIfItHasBeenWithdrawn() { $measure = FlowMeasure::factory()->create(); - $discordNotification = DiscordNotification::factory() + $discordNotification = DivisionDiscordNotification::factory() ->toDivisionWebhook($this->divisionDiscordWebhook) ->create(); $measure->discordNotifications()->attach( @@ -221,7 +221,7 @@ public function testItShouldNotUseDivisionWebhookIfItHasBeenWithdrawn() public function testItShouldNotUseDivisionWebhookIfItHasBeenExpired() { $measure = FlowMeasure::factory()->create(); - $discordNotification = DiscordNotification::factory() + $discordNotification = DivisionDiscordNotification::factory() ->toDivisionWebhook($this->divisionDiscordWebhook) ->create(); $measure->discordNotifications()->attach( diff --git a/tests/TestCase.php b/tests/TestCase.php index a7db7d36..ccacace9 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -13,7 +13,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(); From b21982e6a3fdfcfc77cb1d9cde846768ca10cdf4 Mon Sep 17 00:00:00 2001 From: Andy Ford Date: Tue, 10 Oct 2023 20:39:40 +0100 Subject: [PATCH 05/34] WIP: Start discord notifications --- .env.example | 4 + app/Discord/Client/ClientFactory.php | 32 ++++++ app/Discord/Client/ClientFactoryInterface.php | 10 ++ app/Discord/DiscordServiceInterface.php | 16 +++ app/Discord/DiscordServiceMessageSender.php | 69 ++++++++++++ .../Exception/DiscordServiceException.php | 10 ++ app/Discord/Message/Embed/Embed.php | 38 ++++++- app/Discord/Message/Embed/EmbedCollection.php | 5 + app/Discord/Message/Embed/EmbedInterface.php | 7 ++ app/Providers/DiscordServiceProvider.php | 20 +++- composer.json | 7 +- composer.lock | 93 +++++++++++++++- config/discord.php | 4 +- ...536_create_discord_notifications_table.php | 28 +++++ ...iscord_notification_flow_measure_table.php | 34 ++++++ docker-compose.yml | 58 ++++++---- docker/8.2/Dockerfile | 59 +++++++++++ docker/8.2/php.ini | 4 + docker/8.2/start-container | 17 +++ docker/8.2/supervisord.conf | 14 +++ protobuf | 2 +- .../DiscordServiceMessageSenderTest.php | 100 ++++++++++++++++++ .../Message/Embed/EmbedCollectionTest.php | 19 ++++ tests/Discord/Message/Embed/EmbedTest.php | 95 +++++++++++++++++ tests/Discord/Message/Sender/SenderTest.php | 4 +- 25 files changed, 716 insertions(+), 33 deletions(-) create mode 100644 app/Discord/Client/ClientFactory.php create mode 100644 app/Discord/Client/ClientFactoryInterface.php create mode 100644 app/Discord/DiscordServiceInterface.php create mode 100644 app/Discord/DiscordServiceMessageSender.php create mode 100644 app/Discord/Exception/DiscordServiceException.php create mode 100644 database/migrations/2023_10_08_123536_create_discord_notifications_table.php create mode 100644 database/migrations/2023_10_08_123708_create_discord_notification_flow_measure_table.php create mode 100644 docker/8.2/Dockerfile create mode 100644 docker/8.2/php.ini create mode 100644 docker/8.2/start-container create mode 100644 docker/8.2/supervisord.conf create mode 100644 tests/Discord/DiscordServiceMessageSenderTest.php diff --git a/.env.example b/.env.example index 660691fa..a92082f9 100644 --- a/.env.example +++ b/.env.example @@ -66,3 +66,7 @@ DISCORD_AVATAR_URL="${APP_URL}/images/logo.png" # 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" 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, MessageInterface $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( + [ + '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'); + } + + // TODO: + // Discord message sender can remain the same... + // The "new" sender can remain the same, but of course we give it an "update" method + + // The class "Sender" now only handles division webhooks + + // We create a new set of filters that filter out messages to send based on its ECFMP bot status + // We create a new "associator" type for ECFMP messages + // We create a new sender class for ECFMP messages (for first time sending) + // Updates, we handle outside of the normal flow (e.g. a job) + return $response->getId(); + } +} diff --git a/app/Discord/Exception/DiscordServiceException.php b/app/Discord/Exception/DiscordServiceException.php new file mode 100644 index 00000000..b7a4fc53 --- /dev/null +++ b/app/Discord/Exception/DiscordServiceException.php @@ -0,0 +1,10 @@ +each(function (FieldInterface $field) { + $fields->each(function (FieldInterface $field) + { $this->fields->add($field); }); @@ -97,7 +100,7 @@ public function toArray(): array } if ($this->fields->isNotEmpty()) { - $return['fields'] = $this->fields->map(fn (FieldInterface $field) => [ + $return['fields'] = $this->fields->map(fn(FieldInterface $field) => [ 'name' => $field->name(), 'value' => $field->value(), 'inline' => $field->inline(), @@ -112,4 +115,35 @@ public function toArray(): array return $return; } + + public function toProtobuf(): DiscordEmbeds + { + return tap( + new DiscordEmbeds(), + function (DiscordEmbeds $embed) + { + if (isset($this->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/Providers/DiscordServiceProvider.php b/app/Providers/DiscordServiceProvider.php index 01c9f943..cdf1a91e 100644 --- a/app/Providers/DiscordServiceProvider.php +++ b/app/Providers/DiscordServiceProvider.php @@ -2,8 +2,10 @@ namespace App\Providers; +use App\Discord\Client\ClientFactory; use App\Discord\DiscordInterface; use App\Discord\DiscordMessageSender; +use App\Discord\DiscordServiceMessageSender; use App\Discord\FlowMeasure\Message\FlowMeasureMessageFactory; use App\Discord\FlowMeasure\Message\MessageGenerator; use App\Discord\FlowMeasure\Message\MessageGeneratorInterface; @@ -21,6 +23,8 @@ use App\Repository\FlowMeasureNotification\NotifiedRepository; use App\Repository\FlowMeasureNotification\RepositoryInterface; use App\Repository\FlowMeasureNotification\WithdrawnRepository; +use Ecfmp_discord\DiscordClient; +use Grpc\ChannelCredentials; use Illuminate\Support\ServiceProvider; class DiscordServiceProvider extends ServiceProvider @@ -32,16 +36,22 @@ 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(EcfmpWebhook::class); - $this->app->singleton(Sender::class, fn () => new Sender( - $this->flowMeasureMessageProviders(), - $this->app->make(DiscordInterface::class) - )); + $this->app->singleton( + Sender::class, + fn() => new Sender( + $this->flowMeasureMessageProviders(), + $this->app->make(DiscordInterface::class), + $this->app->make(DiscordServiceMessageSender::class) + ) + ); + + $this->app->singleton(ClientFactory::class); } private function flowMeasureMessageProviders(): array diff --git a/composer.json b/composer.json index ba91fbc6..467c9c7f 100644 --- a/composer.json +++ b/composer.json @@ -9,9 +9,12 @@ "license": "MIT", "require": { "php": "^8.1", + "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", @@ -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 b5244c75..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": "5b2757f4f5ce1d724ad92bd36894217b", + "content-hash": "ea2754f9df7a7351175e772ec656f410", "packages": [ { "name": "akaunting/laravel-money", @@ -1657,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", @@ -1719,6 +1763,50 @@ ], "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.8.0", @@ -13675,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..e2ab8fec 100644 --- a/config/discord.php +++ b/config/discord.php @@ -5,5 +5,7 @@ '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', ''), ]; diff --git a/database/migrations/2023_10_08_123536_create_discord_notifications_table.php b/database/migrations/2023_10_08_123536_create_discord_notifications_table.php new file mode 100644 index 00000000..b41871dc --- /dev/null +++ b/database/migrations/2023_10_08_123536_create_discord_notifications_table.php @@ -0,0 +1,28 @@ +id(); + $table->uuid('remote_id')->unique(); + $table->timestamps(); + }); + } + + /** + * 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..17aadfc0 --- /dev/null +++ b/database/migrations/2023_10_08_123708_create_discord_notification_flow_measure_table.php @@ -0,0 +1,34 @@ +id(); + $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') + ->cascadeOnDelete(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('discord_notification_flow_measure'); + } +}; diff --git a/docker-compose.yml b/docker-compose.yml index 1301cf7f..5701b4c3 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,55 +1,75 @@ # 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: 10 timeout: 100s start_period: 15s start_interval: 1s + + 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/protobuf b/protobuf index a8656fe1..f09fb294 160000 --- a/protobuf +++ b/protobuf @@ -1 +1 @@ -Subproject commit a8656fe1133492e6e72b6fa2020d9028ab12ce94 +Subproject commit f09fb294255fb307a1d785a06a62b3a9b15e8f1e diff --git a/tests/Discord/DiscordServiceMessageSenderTest.php b/tests/Discord/DiscordServiceMessageSenderTest.php new file mode 100644 index 00000000..5e279e8f --- /dev/null +++ b/tests/Discord/DiscordServiceMessageSenderTest.php @@ -0,0 +1,100 @@ +clientFactory = Mockery::mock(ClientFactoryInterface::class); + $this->client = Mockery::mock(DiscordClient::class); + $this->message = Mockery::mock(MessageInterface::class); + $this->embeds = Mockery::mock(EmbedCollection::class); + $this->response = Mockery::mock(CreateResponse::class); + $this->status = Mockery::mock(Status::class); + $this->sender = new DiscordServiceMessageSender($this->clientFactory); + $this->discordEmbeds = Mockery::mock(DiscordEmbeds::class); + + $this->clientFactory->shouldReceive('create')->andReturn($this->client); + } + + 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->client->shouldReceive('Create')->with(Mockery::on( + fn(CreateRequest $request) => $request->getContent() === 'content' + // TODO: See if we can check embeds + ), [ + 'authorization' => [config('discord.service_token')], + 'x-client-request-id' => ['client-request-id'], + ])->andReturn([$this->response, $this->status]); + $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->client->shouldReceive('Create')->with( + Mockery::on( + fn(CreateRequest $request, array $metadata) => $request->getContent() === 'content' + && $request->getEmbeds() === [$this->discordEmbeds] + && $metadata['authorization'] === [config('discord.service_token')] + && $metadata['x-client-request-id'] === ['client-request-id'] + ) + )->andReturn([$this->response, $this->status]); + $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/Message/Embed/EmbedCollectionTest.php b/tests/Discord/Message/Embed/EmbedCollectionTest.php index 2e88441c..310cd3e1 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/SenderTest.php index a04c0a8d..c22ddacc 100644 --- a/tests/Discord/Message/Sender/SenderTest.php +++ b/tests/Discord/Message/Sender/SenderTest.php @@ -51,7 +51,7 @@ public function testItSendsMessages() $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', From 88d22cb35b1f21a000aefc98b018fd392d2e4a37 Mon Sep 17 00:00:00 2001 From: Andy Ford Date: Wed, 11 Oct 2023 06:58:41 +0100 Subject: [PATCH 06/34] dev: add mac metadata to gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 62af7d63..acdaec6c 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,5 @@ public/mix-manifest.json storage/imports .vscode + +.DS_STORE From d9b17a85259a78d192b3cb9537ba5849c02d08e2 Mon Sep 17 00:00:00 2001 From: Andy Ford Date: Wed, 11 Oct 2023 06:59:11 +0100 Subject: [PATCH 07/34] dev: remove unused docker parameter --- docker-compose.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index 5701b4c3..1a673ec4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -46,7 +46,6 @@ services: retries: 10 timeout: 100s start_period: 15s - start_interval: 1s mongodb: extends: From 2c2ca32da3846bd9b3ce284490c41dd02fc05096 Mon Sep 17 00:00:00 2001 From: Andy Ford Date: Wed, 11 Oct 2023 06:59:20 +0100 Subject: [PATCH 08/34] deps: bump protobuf --- protobuf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protobuf b/protobuf index f09fb294..a79f7171 160000 --- a/protobuf +++ b/protobuf @@ -1 +1 @@ -Subproject commit f09fb294255fb307a1d785a06a62b3a9b15e8f1e +Subproject commit a79f7171c726fb68eda294fc032fe1c6987b4051 From 51af00d8a8d265e81138996a6189a8650846d3af Mon Sep 17 00:00:00 2001 From: Andy Ford Date: Wed, 11 Oct 2023 06:59:47 +0100 Subject: [PATCH 09/34] feat: create new messages in discord via service --- app/Discord/DiscordServiceMessageSender.php | 1 + .../DiscordServiceMessageSenderTest.php | 32 +++++++++++-------- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/app/Discord/DiscordServiceMessageSender.php b/app/Discord/DiscordServiceMessageSender.php index df92849b..e321ff1b 100644 --- a/app/Discord/DiscordServiceMessageSender.php +++ b/app/Discord/DiscordServiceMessageSender.php @@ -7,6 +7,7 @@ use App\Discord\Message\MessageInterface; use Ecfmp_discord\CreateRequest; use Log; + use const Grpc\STATUS_OK; class DiscordServiceMessageSender implements DiscordServiceInterface diff --git a/tests/Discord/DiscordServiceMessageSenderTest.php b/tests/Discord/DiscordServiceMessageSenderTest.php index 5e279e8f..716c8393 100644 --- a/tests/Discord/DiscordServiceMessageSenderTest.php +++ b/tests/Discord/DiscordServiceMessageSenderTest.php @@ -4,7 +4,6 @@ use App\Discord\Client\ClientFactoryInterface; use App\Discord\Exception\DiscordServiceException; -use App\Discord\Message\Embed\Embed; use App\Discord\Message\Embed\EmbedCollection; use App\Discord\Message\MessageInterface; use Ecfmp_discord\CreateRequest; @@ -12,6 +11,7 @@ use Ecfmp_discord\DiscordClient; use Ecfmp_discord\DiscordEmbeds; use Grpc\Status; +use Grpc\UnaryCall; use Mockery; use Tests\TestCase; @@ -29,6 +29,8 @@ class DiscordServiceMessageSenderTest extends TestCase private readonly DiscordServiceMessageSender $sender; + private readonly UnaryCall $unaryCall; + public function setUp(): void { parent::setUp(); @@ -39,10 +41,11 @@ public function setUp(): void $this->embeds = Mockery::mock(EmbedCollection::class); $this->response = Mockery::mock(CreateResponse::class); $this->status = Mockery::mock(Status::class); - $this->sender = new DiscordServiceMessageSender($this->clientFactory); $this->discordEmbeds = Mockery::mock(DiscordEmbeds::class); + $this->unaryCall = Mockery::mock(UnaryCall::class); $this->clientFactory->shouldReceive('create')->andReturn($this->client); + $this->sender = new DiscordServiceMessageSender($this->clientFactory); } public function testItThrowsExceptionIfClientIsNotReady() @@ -61,13 +64,15 @@ public function testItSendsAMessageAndReturnsTheId() $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' - // TODO: See if we can check embeds + fn(CreateRequest $request) => $request->getContent() === 'content' && + count($request->getEmbeds()) === 1 && + $request->getEmbeds()[0] == $this->discordEmbeds ), [ 'authorization' => [config('discord.service_token')], 'x-client-request-id' => ['client-request-id'], - ])->andReturn([$this->response, $this->status]); + ])->andReturn($this->unaryCall); $this->status->code = STATUS_OK; $this->response->shouldReceive('getId')->andReturn('id'); @@ -81,14 +86,15 @@ public function testItThrowsAnExceptionIfStatusIsNotOk() $this->message->shouldReceive('content')->andReturn('content'); $this->message->shouldReceive('embeds')->andReturn($this->embeds); $this->embeds->shouldReceive('toProtobuf')->andReturn([$this->discordEmbeds]); - $this->client->shouldReceive('Create')->with( - Mockery::on( - fn(CreateRequest $request, array $metadata) => $request->getContent() === 'content' - && $request->getEmbeds() === [$this->discordEmbeds] - && $metadata['authorization'] === [config('discord.service_token')] - && $metadata['x-client-request-id'] === ['client-request-id'] - ) - )->andReturn([$this->response, $this->status]); + $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 + ), [ + 'authorization' => [config('discord.service_token')], + 'x-client-request-id' => ['client-request-id'], + ])->andReturn($this->unaryCall); $this->status->code = 1; $this->status->details = 'details'; From 40a2df39c4784187a283bc63b2ba39b553b0e8eb Mon Sep 17 00:00:00 2001 From: Andy Ford Date: Wed, 11 Oct 2023 07:00:20 +0100 Subject: [PATCH 10/34] dev: ignore phpunit cache --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index acdaec6c..fa746c90 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,7 @@ Homestead.yaml Homestead.json /.vagrant .phpunit.result.cache +.phpunit.cache .idea # Laravel IDE Helper From bc7e5c72d0d41830ea5602119a59f4887e16ca8e Mon Sep 17 00:00:00 2001 From: Andy Ford Date: Wed, 11 Oct 2023 07:18:45 +0100 Subject: [PATCH 11/34] refactor: rename classes --- ...erface.php => DiscordWebhookInterface.php} | 4 ++-- ...ageSender.php => DiscordWebhookSender.php} | 2 +- .../{Sender.php => DivisionWebhookSender.php} | 12 ++++++------ app/Jobs/SendDiscordNotifications.php | 6 +++--- app/Providers/DiscordServiceProvider.php | 19 ++++++++----------- ...rTest.php => DiscordWebhookSenderTest.php} | 8 ++++---- .../Message/Embed/EmbedCollectionTest.php | 2 +- ...Test.php => DivisionWebhookSenderTest.php} | 10 +++++----- tests/Jobs/SendDiscordNotificationsTest.php | 10 +++++----- 9 files changed, 35 insertions(+), 38 deletions(-) rename app/Discord/{DiscordInterface.php => DiscordWebhookInterface.php} (67%) rename app/Discord/{DiscordMessageSender.php => DiscordWebhookSender.php} (95%) rename app/Discord/Message/Sender/{Sender.php => DivisionWebhookSender.php} (80%) rename tests/Discord/{DiscordMessageSenderTest.php => DiscordWebhookSenderTest.php} (95%) rename tests/Discord/Message/Sender/{SenderTest.php => DivisionWebhookSenderTest.php} (89%) 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/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/Jobs/SendDiscordNotifications.php b/app/Jobs/SendDiscordNotifications.php index 9a72a357..216d45c4 100644 --- a/app/Jobs/SendDiscordNotifications.php +++ b/app/Jobs/SendDiscordNotifications.php @@ -2,7 +2,7 @@ namespace App\Jobs; -use App\Discord\Message\Sender\Sender; +use App\Discord\Message\Sender\DivisionWebhookSender; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldBeUnique; use Illuminate\Contracts\Queue\ShouldQueue; @@ -18,9 +18,9 @@ class SendDiscordNotifications implements ShouldQueue, ShouldBeUnique use Queueable; use SerializesModels; - private readonly Sender $sender; + private readonly DivisionWebhookSender $sender; - public function __construct(Sender $sender) + public function __construct(DivisionWebhookSender $sender) { $this->sender = $sender; } diff --git a/app/Providers/DiscordServiceProvider.php b/app/Providers/DiscordServiceProvider.php index cdf1a91e..b288b377 100644 --- a/app/Providers/DiscordServiceProvider.php +++ b/app/Providers/DiscordServiceProvider.php @@ -3,9 +3,9 @@ namespace App\Providers; use App\Discord\Client\ClientFactory; -use App\Discord\DiscordInterface; -use App\Discord\DiscordMessageSender; use App\Discord\DiscordServiceMessageSender; +use App\Discord\DiscordWebhookInterface; +use App\Discord\DiscordWebhookSender; use App\Discord\FlowMeasure\Message\FlowMeasureMessageFactory; use App\Discord\FlowMeasure\Message\MessageGenerator; use App\Discord\FlowMeasure\Message\MessageGeneratorInterface; @@ -16,15 +16,13 @@ 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; use App\Repository\FlowMeasureNotification\NotifiedRepository; use App\Repository\FlowMeasureNotification\RepositoryInterface; use App\Repository\FlowMeasureNotification\WithdrawnRepository; -use Ecfmp_discord\DiscordClient; -use Grpc\ChannelCredentials; use Illuminate\Support\ServiceProvider; class DiscordServiceProvider extends ServiceProvider @@ -38,16 +36,15 @@ class DiscordServiceProvider extends ServiceProvider 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( + DivisionWebhookSender::class, + fn() => new DivisionWebhookSender( $this->flowMeasureMessageProviders(), - $this->app->make(DiscordInterface::class), - $this->app->make(DiscordServiceMessageSender::class) + $this->app->make(DiscordWebhookInterface::class) ) ); 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/Message/Embed/EmbedCollectionTest.php b/tests/Discord/Message/Embed/EmbedCollectionTest.php index 310cd3e1..6f6a0811 100644 --- a/tests/Discord/Message/Embed/EmbedCollectionTest.php +++ b/tests/Discord/Message/Embed/EmbedCollectionTest.php @@ -46,7 +46,7 @@ public function testItMapsEmbedsToProtobuf() $embeds = (new EmbedCollection()) ->add(Embed::make()->withTitle($mockTitle1)) ->add(Embed::make()->withTitle($mockTitle2)) - ->toProtobuf() + ->toProtobuf(); $this->assertCount(2, $embeds); diff --git a/tests/Discord/Message/Sender/SenderTest.php b/tests/Discord/Message/Sender/DivisionWebhookSenderTest.php similarity index 89% rename from tests/Discord/Message/Sender/SenderTest.php rename to tests/Discord/Message/Sender/DivisionWebhookSenderTest.php index c22ddacc..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,11 +43,11 @@ 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( diff --git a/tests/Jobs/SendDiscordNotificationsTest.php b/tests/Jobs/SendDiscordNotificationsTest.php index d1473fde..28fca9ed 100644 --- a/tests/Jobs/SendDiscordNotificationsTest.php +++ b/tests/Jobs/SendDiscordNotificationsTest.php @@ -2,7 +2,7 @@ namespace Tests\Jobs; -use App\Discord\Message\Sender\Sender; +use App\Discord\Message\Sender\DivisionWebhookSender; use App\Jobs\SendDiscordNotifications; use Illuminate\Support\Facades\Config; use Mockery; @@ -14,9 +14,9 @@ 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); $job = $this->app->make(SendDiscordNotifications::class); $job->handle(); @@ -26,9 +26,9 @@ 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); $job = $this->app->make(SendDiscordNotifications::class); $job->handle(); From cd9e90fd5d0c9a647b9e7a7dd1beb62b97d25313 Mon Sep 17 00:00:00 2001 From: Andy Ford Date: Wed, 11 Oct 2023 07:25:43 +0100 Subject: [PATCH 12/34] refactor: rename relation --- .../Associator/FlowMeasureAssociator.php | 2 +- app/Models/FlowMeasure.php | 4 ++-- .../ActiveRepository.php | 2 +- .../ExpiredRepository.php | 2 +- .../NotifiedRepository.php | 2 +- .../WithdrawnRepository.php | 2 +- .../FlowMeasureRecipientsFactoryTest.php | 8 +++---- .../Helper/NotificationReissuerTest.php | 24 +++++++++---------- .../Filter/ActivatedWebhookFilterTest.php | 12 +++++----- .../Filter/ExpiredWebhookFilterTest.php | 16 ++++++------- .../Filter/NotifiedWebhookFilterTest.php | 12 +++++----- .../Filter/WithdrawnWebhookFilterTest.php | 24 +++++++++---------- 12 files changed, 55 insertions(+), 55 deletions(-) diff --git a/app/Discord/FlowMeasure/Associator/FlowMeasureAssociator.php b/app/Discord/FlowMeasure/Associator/FlowMeasureAssociator.php index 9885b5b2..ae159598 100644 --- a/app/Discord/FlowMeasure/Associator/FlowMeasureAssociator.php +++ b/app/Discord/FlowMeasure/Associator/FlowMeasureAssociator.php @@ -22,7 +22,7 @@ public function __construct(FlowMeasure $flowMeasure, DiscordNotificationTypeEnu public function associate(DivisionDiscordNotification $notification): void { - $this->flowMeasure->discordNotifications()->attach( + $this->flowMeasure->divisionDiscordNotifications()->attach( [ $notification->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum($this->type), diff --git a/app/Models/FlowMeasure.php b/app/Models/FlowMeasure.php index d2dbcf7e..796474ca 100644 --- a/app/Models/FlowMeasure.php +++ b/app/Models/FlowMeasure.php @@ -116,7 +116,7 @@ public function scopeFlightInformationRegion( return $query->where('flight_information_region_id', $flightInformationRegion->id); } - public function discordNotifications(): BelongsToMany + public function divisionDiscordNotifications(): BelongsToMany { return $this->belongsToMany(DivisionDiscordNotification::class) ->withPivot(['discord_notification_type_id', 'notified_as']) @@ -165,7 +165,7 @@ public function activatedAndNotifiedNotifications(): BelongsToMany private function notificationsOfType(array $types): BelongsToMany { - return $this->discordNotifications() + return $this->divisionDiscordNotifications() ->wherePivotIn( 'discord_notification_type_id', DiscordNotificationType::whereIn( diff --git a/app/Repository/FlowMeasureNotification/ActiveRepository.php b/app/Repository/FlowMeasureNotification/ActiveRepository.php index 58370ea3..5266b20a 100644 --- a/app/Repository/FlowMeasureNotification/ActiveRepository.php +++ b/app/Repository/FlowMeasureNotification/ActiveRepository.php @@ -10,7 +10,7 @@ class ActiveRepository implements RepositoryInterface { 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..b787bc71 100644 --- a/app/Repository/FlowMeasureNotification/ExpiredRepository.php +++ b/app/Repository/FlowMeasureNotification/ExpiredRepository.php @@ -10,7 +10,7 @@ class ExpiredRepository implements RepositoryInterface { public function flowMeasuresForNotification(): Collection { - return FlowMeasure::with('discordNotifications') + return FlowMeasure::with('divisionDiscordNotifications') ->expiredRecently() ->get(); } diff --git a/app/Repository/FlowMeasureNotification/NotifiedRepository.php b/app/Repository/FlowMeasureNotification/NotifiedRepository.php index 589ec1d1..004a421f 100644 --- a/app/Repository/FlowMeasureNotification/NotifiedRepository.php +++ b/app/Repository/FlowMeasureNotification/NotifiedRepository.php @@ -11,7 +11,7 @@ class NotifiedRepository implements RepositoryInterface { 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/WithdrawnRepository.php b/app/Repository/FlowMeasureNotification/WithdrawnRepository.php index d7da842a..0b0d1b83 100644 --- a/app/Repository/FlowMeasureNotification/WithdrawnRepository.php +++ b/app/Repository/FlowMeasureNotification/WithdrawnRepository.php @@ -20,7 +20,7 @@ public function flowMeasuresForNotification(): Collection private function baseQuery(): Builder { - return FlowMeasure::with('discordNotifications') + return FlowMeasure::with('divisionDiscordNotifications') ->onlyTrashed() ->where('deleted_at', '>', Carbon::now()->subHour()); } diff --git a/tests/Discord/FlowMeasure/Content/FlowMeasureRecipientsFactoryTest.php b/tests/Discord/FlowMeasure/Content/FlowMeasureRecipientsFactoryTest.php index 1b4b62c8..cd5c193b 100644 --- a/tests/Discord/FlowMeasure/Content/FlowMeasureRecipientsFactoryTest.php +++ b/tests/Discord/FlowMeasure/Content/FlowMeasureRecipientsFactoryTest.php @@ -157,7 +157,7 @@ public function testItReturnsNoRecipientsIfNotifiedRecently() $this->flowMeasure->notifiedFlightInformationRegions()->sync([$fir->id]); $this->webhook->shouldReceive('id')->andReturn(null); - $notification = $this->flowMeasure->discordNotifications()->create( + $notification = $this->flowMeasure->divisionDiscordNotifications()->create( [ 'content' => '', 'embeds' => [], @@ -186,7 +186,7 @@ public function testItReturnsRecipientsIfNotifiedALongTimeAgo() $this->flowMeasure->notifiedFlightInformationRegions()->sync([$fir->id]); $this->webhook->shouldReceive('id')->andReturn(null); - $notification = $this->flowMeasure->discordNotifications()->create( + $notification = $this->flowMeasure->divisionDiscordNotifications()->create( [ 'content' => '', 'embeds' => [], @@ -216,7 +216,7 @@ public function testItReturnsRecipientsIfNotifiedAsReissue() $this->flowMeasure->notifiedFlightInformationRegions()->sync([$fir->id]); $this->webhook->shouldReceive('id')->andReturn(null); - $notification = $this->flowMeasure->discordNotifications()->create( + $notification = $this->flowMeasure->divisionDiscordNotifications()->create( [ 'content' => '', 'embeds' => [], @@ -246,7 +246,7 @@ public function testItReturnsRecipientsIfNotActivating() $this->flowMeasure->notifiedFlightInformationRegions()->sync([$fir->id]); $this->webhook->shouldReceive('id')->andReturn(null); - $notification = $this->flowMeasure->discordNotifications()->create( + $notification = $this->flowMeasure->divisionDiscordNotifications()->create( [ 'content' => '', 'embeds' => [], diff --git a/tests/Discord/FlowMeasure/Helper/NotificationReissuerTest.php b/tests/Discord/FlowMeasure/Helper/NotificationReissuerTest.php index ed3aae7b..143ef2a4 100644 --- a/tests/Discord/FlowMeasure/Helper/NotificationReissuerTest.php +++ b/tests/Discord/FlowMeasure/Helper/NotificationReissuerTest.php @@ -52,7 +52,7 @@ public function testItHasAFlowMeasure() public function testItsAReissueIfItsNotifiedAndTheIdentifierHasChanged() { $previousNotification = DivisionDiscordNotification::factory()->create(); - $this->flowMeasure->discordNotifications()->sync( + $this->flowMeasure->divisionDiscordNotifications()->sync( [ $previousNotification->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -77,7 +77,7 @@ public function testItsAReissueIfItsNotifiedAndTheIdentifierHasChanged() public function testItsAReissueIfItsActivatedAndTheIdentifierHasChanged() { $previousNotification = DivisionDiscordNotification::factory()->create(); - $this->flowMeasure->discordNotifications()->sync( + $this->flowMeasure->divisionDiscordNotifications()->sync( [ $previousNotification->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -102,7 +102,7 @@ public function testItsAReissueIfItsActivatedAndTheIdentifierHasChanged() public function testItsAReissueIfItWasNotifiedAndTheIdentifierHasChangedForActivation() { $previousNotification = DivisionDiscordNotification::factory()->create(); - $this->flowMeasure->discordNotifications()->sync( + $this->flowMeasure->divisionDiscordNotifications()->sync( [ $previousNotification->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -134,7 +134,7 @@ public function testItsAReissueIfItsNotifiedOnAEcfmpWebhook() ->toDivisionWebhook($divisionWebhook) ->create(); - $this->flowMeasure->discordNotifications()->sync( + $this->flowMeasure->divisionDiscordNotifications()->sync( [ $previousNotificationEcfmp->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -174,7 +174,7 @@ public function testItsAReissueIfItsNotifiedOnADifferentDivisionWebhook() ->toDivisionWebhook($divisionWebhook) ->create(); - $this->flowMeasure->discordNotifications()->sync( + $this->flowMeasure->divisionDiscordNotifications()->sync( [ $previousNotificationOtherDivision->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -214,7 +214,7 @@ public function testItsAReissueIfItsActivatedOnADifferentDivisionWebhook() ->toDivisionWebhook($divisionWebhook) ->create(); - $this->flowMeasure->discordNotifications()->sync( + $this->flowMeasure->divisionDiscordNotifications()->sync( [ $previousNotificationOtherDivision->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -247,7 +247,7 @@ public function testItsAReissueIfItsActivatedOnAEcfmpWebhook() $previousNotification = DivisionDiscordNotification::factory() ->create(); - $this->flowMeasure->discordNotifications()->sync( + $this->flowMeasure->divisionDiscordNotifications()->sync( [ $previousNotification->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -272,7 +272,7 @@ public function testItsAReissueIfItsActivatedOnAEcfmpWebhook() public function testItIsNotAReissueIfItsNotifiedAndTheIdentifierHasNotChanged() { $previousNotification = DivisionDiscordNotification::factory()->create(); - $this->flowMeasure->discordNotifications()->sync( + $this->flowMeasure->divisionDiscordNotifications()->sync( [ $previousNotification->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -297,7 +297,7 @@ public function testItIsNotAReissueIfItsNotifiedAndTheIdentifierHasNotChanged() public function testItIsNotAReissueIfItsActivatedAndTheIdentifierHasNotChanged() { $previousNotification = DivisionDiscordNotification::factory()->create(); - $this->flowMeasure->discordNotifications()->sync( + $this->flowMeasure->divisionDiscordNotifications()->sync( [ $previousNotification->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -322,7 +322,7 @@ public function testItIsNotAReissueIfItsActivatedAndTheIdentifierHasNotChanged() public function testItIsNotAReissueIfItsNotifiedAndThenActivatedTheIdentifierHasNotChanged() { $previousNotification = DivisionDiscordNotification::factory()->create(); - $this->flowMeasure->discordNotifications()->sync( + $this->flowMeasure->divisionDiscordNotifications()->sync( [ $previousNotification->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -373,7 +373,7 @@ public function testItIsNotAReissueIfItsNotifiedAndNeverBeenActivated() public function testItIsNotAReissueIfItsWithdrawn() { $previousNotification = DivisionDiscordNotification::factory()->create(); - $this->flowMeasure->discordNotifications()->sync( + $this->flowMeasure->divisionDiscordNotifications()->sync( [ $previousNotification->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -398,7 +398,7 @@ public function testItIsNotAReissueIfItsWithdrawn() public function testItIsNotAReissueIfItsExpired() { $previousNotification = DivisionDiscordNotification::factory()->create(); - $this->flowMeasure->discordNotifications()->sync( + $this->flowMeasure->divisionDiscordNotifications()->sync( [ $previousNotification->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( diff --git a/tests/Discord/FlowMeasure/Webhook/Filter/ActivatedWebhookFilterTest.php b/tests/Discord/FlowMeasure/Webhook/Filter/ActivatedWebhookFilterTest.php index 4fff3c7c..94f9877f 100644 --- a/tests/Discord/FlowMeasure/Webhook/Filter/ActivatedWebhookFilterTest.php +++ b/tests/Discord/FlowMeasure/Webhook/Filter/ActivatedWebhookFilterTest.php @@ -40,7 +40,7 @@ public function testItShouldUseWebhookIfFlowMeasureHasOnlyBeenNotifiedToEcfmpWeb { $measure = FlowMeasure::factory()->create(); $discordNotification = DivisionDiscordNotification::factory()->create(); - $measure->discordNotifications()->attach( + $measure->divisionDiscordNotifications()->attach( [ $discordNotification->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -63,7 +63,7 @@ public function testItShouldUseWebhookIfFlowMeasureHasBeenActivatedAsDifferentId { $measure = FlowMeasure::factory()->create(); $discordNotification = DivisionDiscordNotification::factory()->create(); - $measure->discordNotifications()->attach( + $measure->divisionDiscordNotifications()->attach( [ $discordNotification->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -86,7 +86,7 @@ public function testItShouldNotUseWebhookIfFlowMeasureHasBeenActivatedToEcfmpWeb { $measure = FlowMeasure::factory()->create(); $discordNotification = DivisionDiscordNotification::factory()->create(); - $measure->discordNotifications()->attach( + $measure->divisionDiscordNotifications()->attach( [ $discordNotification->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -122,7 +122,7 @@ public function testItShouldUseWebhookIfFlowMeasureHasOnlyBeenNotifiedToDivision $discordNotification = DivisionDiscordNotification::factory() ->toDivisionWebhook($this->divisionDiscordWebhook) ->create(); - $measure->discordNotifications()->attach( + $measure->divisionDiscordNotifications()->attach( [ $discordNotification->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -147,7 +147,7 @@ public function testItShouldUseWebhookIfFlowMeasureHasBeenActivatedAsDifferentId $discordNotification = DivisionDiscordNotification::factory() ->toDivisionWebhook($this->divisionDiscordWebhook) ->create(); - $measure->discordNotifications()->attach( + $measure->divisionDiscordNotifications()->attach( [ $discordNotification->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -172,7 +172,7 @@ public function testItShouldNotUseWebhookIfFlowMeasureHasBeenActivatedToDivision $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 0c6de4f9..13ce3250 100644 --- a/tests/Discord/FlowMeasure/Webhook/Filter/ExpiredWebhookFilterTest.php +++ b/tests/Discord/FlowMeasure/Webhook/Filter/ExpiredWebhookFilterTest.php @@ -72,7 +72,7 @@ public function testItShouldUseWebhookIfFlowMeasureHasOnlyBeenNotifiedToEcfmpWeb { $measure = FlowMeasure::factory()->create(['identifier' => 'EGTT23A-3']); $discordNotification = DivisionDiscordNotification::factory()->create(); - $measure->discordNotifications()->attach( + $measure->divisionDiscordNotifications()->attach( [ $discordNotification->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -95,7 +95,7 @@ public function testItShouldUseWebhookIfFlowMeasureHasOnlyBeenActivatedToEcfmpWe { $measure = FlowMeasure::factory()->create(['identifier' => 'EGTT23A-3']); $discordNotification = DivisionDiscordNotification::factory()->create(); - $measure->discordNotifications()->attach( + $measure->divisionDiscordNotifications()->attach( [ $discordNotification->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -118,7 +118,7 @@ public function testItShouldNotUseWebhookIfFlowMeasureHasBeenWithdrawnToEcfmpWeb { $measure = FlowMeasure::factory()->create(['identifier' => 'EGTT23A-3']); $discordNotification = DivisionDiscordNotification::factory()->create(); - $measure->discordNotifications()->attach( + $measure->divisionDiscordNotifications()->attach( [ $discordNotification->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -141,7 +141,7 @@ public function testItShouldNotUseWebhookIfFlowMeasureHasBeenExpiredToEcfmpWebho { $measure = FlowMeasure::factory()->create(['identifier' => 'EGTT23A-3']); $discordNotification = DivisionDiscordNotification::factory()->create(); - $measure->discordNotifications()->attach( + $measure->divisionDiscordNotifications()->attach( [ $discordNotification->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -191,7 +191,7 @@ public function testItShouldNotUseWebhookIfFlowMeasureHasOnlyBeenNotifiedToDivis $discordNotification = DivisionDiscordNotification::factory() ->toDivisionWebhook($this->divisionDiscordWebhook) ->create(); - $measure->discordNotifications()->attach( + $measure->divisionDiscordNotifications()->attach( [ $discordNotification->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -216,7 +216,7 @@ public function testItShouldNotUseWebhookIfFlowMeasureHasOnlyBeenActivatedToDivi $discordNotification = DivisionDiscordNotification::factory() ->toDivisionWebhook($this->divisionDiscordWebhook) ->create(); - $measure->discordNotifications()->attach( + $measure->divisionDiscordNotifications()->attach( [ $discordNotification->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -241,7 +241,7 @@ public function testItShouldNotUseWebhookIfFlowMeasureHasBeenExpiredToDivisionWe $discordNotification = DivisionDiscordNotification::factory() ->toDivisionWebhook($this->divisionDiscordWebhook) ->create(); - $measure->discordNotifications()->attach( + $measure->divisionDiscordNotifications()->attach( [ $discordNotification->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -266,7 +266,7 @@ public function testItShouldNotUseWebhookIfFlowMeasureHasBeenWithdrawnToDivision $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 f507f6d2..fbace8a0 100644 --- a/tests/Discord/FlowMeasure/Webhook/Filter/NotifiedWebhookFilterTest.php +++ b/tests/Discord/FlowMeasure/Webhook/Filter/NotifiedWebhookFilterTest.php @@ -40,7 +40,7 @@ public function testItShouldUseWebhookIfFlowMeasureHasBeenNotifiedToEcfmpWebhook { $measure = FlowMeasure::factory()->create(); $discordNotification = DivisionDiscordNotification::factory()->create(); - $measure->discordNotifications()->attach( + $measure->divisionDiscordNotifications()->attach( [ $discordNotification->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -63,7 +63,7 @@ public function testItShouldNotUseWebhookIfFlowMeasureHasBeenNotifiedToEcfmpWebh { $measure = FlowMeasure::factory()->create(); $discordNotification = DivisionDiscordNotification::factory()->create(); - $measure->discordNotifications()->attach( + $measure->divisionDiscordNotifications()->attach( [ $discordNotification->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -86,7 +86,7 @@ public function testItShouldNotUseWebhookIfFlowMeasureHasBeenActivatedToEcfmpWeb { $measure = FlowMeasure::factory()->create(); $discordNotification = DivisionDiscordNotification::factory()->create(); - $measure->discordNotifications()->attach( + $measure->divisionDiscordNotifications()->attach( [ $discordNotification->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -122,7 +122,7 @@ public function testItShouldUseWebhookIfFlowMeasureHasOnlyBeenNotifiedToDivision $discordNotification = DivisionDiscordNotification::factory() ->toDivisionWebhook($this->divisionDiscordWebhook) ->create(); - $measure->discordNotifications()->attach( + $measure->divisionDiscordNotifications()->attach( [ $discordNotification->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -147,7 +147,7 @@ public function testItShouldNotUseWebhookIfFlowMeasureHasBeenNotifiedToDivisionW $discordNotification = DivisionDiscordNotification::factory() ->toDivisionWebhook($this->divisionDiscordWebhook) ->create(); - $measure->discordNotifications()->attach( + $measure->divisionDiscordNotifications()->attach( [ $discordNotification->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -172,7 +172,7 @@ public function testItShouldNotUseWebhookIfFlowMeasureHasBeenActivatedToDivision $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 e95ca3a2..bb8d5e4c 100644 --- a/tests/Discord/FlowMeasure/Webhook/Filter/WithdrawnWebhookFilterTest.php +++ b/tests/Discord/FlowMeasure/Webhook/Filter/WithdrawnWebhookFilterTest.php @@ -29,7 +29,7 @@ public function testItShouldUseEcfmpWebhookIfItHasBeenNotified() { $measure = FlowMeasure::factory()->create(); $discordNotification = DivisionDiscordNotification::factory()->create(); - $measure->discordNotifications()->attach( + $measure->divisionDiscordNotifications()->attach( [ $discordNotification->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -51,7 +51,7 @@ public function testItShouldUseEcfmpWebhookIfItHasBeenActivated() { $measure = FlowMeasure::factory()->create(); $discordNotification = DivisionDiscordNotification::factory()->create(); - $measure->discordNotifications()->attach( + $measure->divisionDiscordNotifications()->attach( [ $discordNotification->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -73,7 +73,7 @@ public function testItShouldNotUseEcfmpWebhookIfItHasBeenWithdrawn() { $measure = FlowMeasure::factory()->create(); $discordNotification = DivisionDiscordNotification::factory()->create(); - $measure->discordNotifications()->attach( + $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( @@ -106,7 +106,7 @@ public function testItShouldNotUseEcfmpWebhookIfItHasBeenExpired() { $measure = FlowMeasure::factory()->create(); $discordNotification = DivisionDiscordNotification::factory()->create(); - $measure->discordNotifications()->attach( + $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( @@ -141,7 +141,7 @@ public function testItShouldUseDivisionWebhookIfItHasBeenNotified() $discordNotification = DivisionDiscordNotification::factory() ->toDivisionWebhook($this->divisionDiscordWebhook) ->create(); - $measure->discordNotifications()->attach( + $measure->divisionDiscordNotifications()->attach( [ $discordNotification->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -165,7 +165,7 @@ public function testItShouldUseDivisionWebhookIfItHasBeenActivated() $discordNotification = DivisionDiscordNotification::factory() ->toDivisionWebhook($this->divisionDiscordWebhook) ->create(); - $measure->discordNotifications()->attach( + $measure->divisionDiscordNotifications()->attach( [ $discordNotification->id => [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -189,7 +189,7 @@ public function testItShouldNotUseDivisionWebhookIfItHasBeenWithdrawn() $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( @@ -224,7 +224,7 @@ public function testItShouldNotUseDivisionWebhookIfItHasBeenExpired() $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( From 2c7963ef569a9462f8712a2c50b2912e437f5fc0 Mon Sep 17 00:00:00 2001 From: Andy Ford Date: Wed, 11 Oct 2023 07:56:10 +0100 Subject: [PATCH 13/34] refactor: more classname refactoring --- .../Content/FlowMeasureRecipientsFactory.php | 2 +- .../FlowMeasure/Embed/NotifiedEmbeds.php | 2 +- ...php => DivisionWebhookMessageProvider.php} | 2 +- .../Provider/PendingDiscordMessage.php | 4 +- .../Provider/PendingMessageInterface.php | 7 +++- .../FlowMeasureRecipientsFactoryTest.php | 41 +++++++++++++++---- .../FlowMeasure/Embed/NotifiedEmbedsTest.php | 4 +- ...=> DivisionWebhookMessageProviderTest.php} | 8 ++-- .../Provider/PendingDiscordMessageTest.php | 0 9 files changed, 49 insertions(+), 21 deletions(-) rename app/Discord/FlowMeasure/Provider/{MessageProvider.php => DivisionWebhookMessageProvider.php} (95%) rename tests/Discord/FlowMeasure/Provider/{MessageProviderTest.php => DivisionWebhookMessageProviderTest.php} (90%) rename {app => tests}/Discord/FlowMeasure/Provider/PendingDiscordMessageTest.php (100%) diff --git a/app/Discord/FlowMeasure/Content/FlowMeasureRecipientsFactory.php b/app/Discord/FlowMeasure/Content/FlowMeasureRecipientsFactory.php index 5daddc28..344cf3b1 100644 --- a/app/Discord/FlowMeasure/Content/FlowMeasureRecipientsFactory.php +++ b/app/Discord/FlowMeasure/Content/FlowMeasureRecipientsFactory.php @@ -19,7 +19,7 @@ public function makeRecipients(PendingMessageInterface $pendingMessage): FlowMea return new NoRecipients(); } - return $pendingMessage->webhook()->id() === null + return $pendingMessage->webhook() === null ? $this->ecfmpRecipients($pendingMessage) : $this->divisionRecipients($pendingMessage); } diff --git a/app/Discord/FlowMeasure/Embed/NotifiedEmbeds.php b/app/Discord/FlowMeasure/Embed/NotifiedEmbeds.php index 397dcd13..9950e4e5 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())), is_null($this->pendingMessage->webhook())) ->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/Provider/MessageProvider.php b/app/Discord/FlowMeasure/Provider/DivisionWebhookMessageProvider.php similarity index 95% rename from app/Discord/FlowMeasure/Provider/MessageProvider.php rename to app/Discord/FlowMeasure/Provider/DivisionWebhookMessageProvider.php index 6e249b66..94b30083 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; diff --git a/app/Discord/FlowMeasure/Provider/PendingDiscordMessage.php b/app/Discord/FlowMeasure/Provider/PendingDiscordMessage.php index 3d7fef81..49888f1e 100644 --- a/app/Discord/FlowMeasure/Provider/PendingDiscordMessage.php +++ b/app/Discord/FlowMeasure/Provider/PendingDiscordMessage.php @@ -11,13 +11,13 @@ class PendingDiscordMessage implements PendingMessageInterface { private readonly FlowMeasure $measure; private readonly DiscordNotificationType $type; - private readonly WebhookInterface $webhook; + private readonly ?WebhookInterface $webhook; private readonly NotificationReissuerInterface $resissue; public function __construct( FlowMeasure $measure, DiscordNotificationType $type, - WebhookInterface $webhook, + ?WebhookInterface $webhook, NotificationReissuerInterface $resissue ) { $this->measure = $measure; diff --git a/app/Discord/FlowMeasure/Provider/PendingMessageInterface.php b/app/Discord/FlowMeasure/Provider/PendingMessageInterface.php index 42985f1a..95f09dac 100644 --- a/app/Discord/FlowMeasure/Provider/PendingMessageInterface.php +++ b/app/Discord/FlowMeasure/Provider/PendingMessageInterface.php @@ -7,13 +7,16 @@ 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 webhook(): ?WebhookInterface; } diff --git a/tests/Discord/FlowMeasure/Content/FlowMeasureRecipientsFactoryTest.php b/tests/Discord/FlowMeasure/Content/FlowMeasureRecipientsFactoryTest.php index cd5c193b..f037de7b 100644 --- a/tests/Discord/FlowMeasure/Content/FlowMeasureRecipientsFactoryTest.php +++ b/tests/Discord/FlowMeasure/Content/FlowMeasureRecipientsFactoryTest.php @@ -32,9 +32,6 @@ public function setUp(): void $this->flowMeasure = FlowMeasure::factory()->create(); $this->webhook = Mockery::mock(WebhookInterface::class); $this->pendingMessage = Mockery::mock(PendingMessageInterface::class); - $this->pendingMessage - ->shouldReceive('webhook') - ->andReturn($this->webhook); $this->pendingMessage ->shouldReceive('flowMeasure') ->andReturn($this->flowMeasure); @@ -46,6 +43,9 @@ public function testItReturnsNoDivisionRecipients() ->shouldReceive('type') ->andReturn(DiscordNotificationTypeEnum::FLOW_MEASURE_ACTIVATED); $divisionWebhook = DivisionDiscordWebhook::factory()->create(); + $this->pendingMessage + ->shouldReceive('webhook') + ->andReturn($this->webhook); $this->webhook->shouldReceive('id')->andReturn($divisionWebhook->id); $this->assertInstanceOf(NoRecipients::class, $this->factory->makeRecipients($this->pendingMessage)); @@ -61,6 +61,9 @@ public function testItReturnsNoDivisionRecipientsIfTagNull() $divisionWebhook->flightInformationRegions()->sync([$fir->id => ['tag' => null]]); $this->flowMeasure->notifiedFlightInformationRegions()->sync([$fir->id]); + $this->pendingMessage + ->shouldReceive('webhook') + ->andReturn($this->webhook); $this->webhook->shouldReceive('id')->andReturn($divisionWebhook->id); $this->assertInstanceOf(NoRecipients::class, $this->factory->makeRecipients($this->pendingMessage)); @@ -76,6 +79,9 @@ public function testItReturnsNoDivisionRecipientsIfTagEmptyString() $divisionWebhook->flightInformationRegions()->sync([$fir->id => ['tag' => '']]); $this->flowMeasure->notifiedFlightInformationRegions()->sync([$fir->id]); + $this->pendingMessage + ->shouldReceive('webhook') + ->andReturn($this->webhook); $this->webhook->shouldReceive('id')->andReturn($divisionWebhook->id); $this->assertInstanceOf(NoRecipients::class, $this->factory->makeRecipients($this->pendingMessage)); @@ -89,6 +95,9 @@ public function testItReturnsDivisionRecipients() $divisionWebhook = DivisionDiscordWebhook::factory()->create(); $fir = FlightInformationRegion::factory()->create(); $divisionWebhook->flightInformationRegions()->sync([$fir->id => ['tag' => '1234']]); + $this->pendingMessage + ->shouldReceive('webhook') + ->andReturn($this->webhook); $this->webhook->shouldReceive('id')->andReturn($divisionWebhook->id); $this->flowMeasure->notifiedFlightInformationRegions()->sync([$fir->id]); @@ -107,6 +116,9 @@ public function testItReturnsMultipleDivisionRecipients() $divisionWebhook->flightInformationRegions()->attach([$fir->id => ['tag' => '1234']]); $fir2 = FlightInformationRegion::factory()->create(); $divisionWebhook->flightInformationRegions()->attach([$fir2->id => ['tag' => '5678']]); + $this->pendingMessage + ->shouldReceive('webhook') + ->andReturn($this->webhook); $this->webhook->shouldReceive('id')->andReturn($divisionWebhook->id); $this->flowMeasure->notifiedFlightInformationRegions()->sync([$fir->id, $fir2->id]); @@ -125,6 +137,9 @@ public function testItIgnoresRecipientsThatAreNotForFlowMeasure() $divisionWebhook->flightInformationRegions()->attach([$fir->id => ['tag' => '1234']]); $fir2 = FlightInformationRegion::factory()->create(); $divisionWebhook->flightInformationRegions()->attach([$fir2->id => ['tag' => '5678']]); + $this->pendingMessage + ->shouldReceive('webhook') + ->andReturn($this->webhook); $this->webhook->shouldReceive('id')->andReturn($divisionWebhook->id); $this->flowMeasure->notifiedFlightInformationRegions()->sync([$fir2->id]); @@ -141,7 +156,9 @@ public function testItReturnsEcfmpRecipients() $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); + $this->pendingMessage + ->shouldReceive('webhook') + ->andReturn(null); $recipients = $this->factory->makeRecipients($this->pendingMessage); $this->assertInstanceOf(EcfmpInterestedParties::class, $recipients); @@ -155,7 +172,9 @@ public function testItReturnsNoRecipientsIfNotifiedRecently() ->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); + $this->pendingMessage + ->shouldReceive('webhook') + ->andReturn(null); $notification = $this->flowMeasure->divisionDiscordNotifications()->create( [ @@ -184,7 +203,9 @@ public function testItReturnsRecipientsIfNotifiedALongTimeAgo() $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); + $this->pendingMessage + ->shouldReceive('webhook') + ->andReturn(null); $notification = $this->flowMeasure->divisionDiscordNotifications()->create( [ @@ -214,7 +235,9 @@ public function testItReturnsRecipientsIfNotifiedAsReissue() $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); + $this->pendingMessage + ->shouldReceive('webhook') + ->andReturn(null); $notification = $this->flowMeasure->divisionDiscordNotifications()->create( [ @@ -244,7 +267,9 @@ public function testItReturnsRecipientsIfNotActivating() $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); + $this->pendingMessage + ->shouldReceive('webhook') + ->andReturn(null); $notification = $this->flowMeasure->divisionDiscordNotifications()->create( [ diff --git a/tests/Discord/FlowMeasure/Embed/NotifiedEmbedsTest.php b/tests/Discord/FlowMeasure/Embed/NotifiedEmbedsTest.php index 7384d31e..b59f7651 100644 --- a/tests/Discord/FlowMeasure/Embed/NotifiedEmbedsTest.php +++ b/tests/Discord/FlowMeasure/Embed/NotifiedEmbedsTest.php @@ -43,7 +43,7 @@ public function testItHasEmbeds() $this->pendingMessage->shouldReceive('flowMeasure')->andReturn($measure); $this->pendingMessage->shouldReceive('reissue')->andReturn($this->reissuer); - $this->pendingMessage->shouldReceive('webhook')->andReturn($this->webhook); + $this->pendingMessage->shouldReceive('webhook')->andReturn(null); $this->reissuer->shouldReceive('isReissuedNotification')->andReturn(false); $this->webhook->shouldReceive('id')->andReturnNull(); @@ -118,7 +118,7 @@ public function testItHasEmbedsWhenReissued() $this->pendingMessage->shouldReceive('flowMeasure')->andReturn($measure); $this->pendingMessage->shouldReceive('reissue')->andReturn($this->reissuer); - $this->pendingMessage->shouldReceive('webhook')->andReturn($this->webhook); + $this->pendingMessage->shouldReceive('webhook')->andReturn(null); $this->reissuer->shouldReceive('isReissuedNotification')->andReturn(true); $this->webhook->shouldReceive('id')->andReturnNull(); 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/PendingDiscordMessageTest.php similarity index 100% rename from app/Discord/FlowMeasure/Provider/PendingDiscordMessageTest.php rename to tests/Discord/FlowMeasure/Provider/PendingDiscordMessageTest.php From 624b56398587fb2f2929d634d7a5d822af67eb19 Mon Sep 17 00:00:00 2001 From: Andy Ford Date: Wed, 11 Oct 2023 07:56:28 +0100 Subject: [PATCH 14/34] style: pint --- app/Discord/DiscordServiceInterface.php | 4 ++-- .../Exception/DiscordServiceException.php | 1 - .../Content/FlowMeasureRecipientsFactory.php | 16 ++++++++-------- .../FlowMeasure/Helper/NotificationReissuer.php | 10 +++++----- .../Provider/PendingMessageInterface.php | 2 +- .../Webhook/Filter/ExpiredWebhookFilter.php | 2 +- app/Discord/Message/Embed/Embed.php | 10 ++++------ app/Models/FlowMeasure.php | 7 +++---- app/Providers/DiscordServiceProvider.php | 3 +-- ...12953_rename_discord_notifications_tables.php | 3 +-- ..._discord_notification_flow_measures_table.php | 3 +-- ...123536_create_discord_notifications_table.php | 3 +-- ...e_discord_notification_flow_measure_table.php | 6 ++---- .../Discord/DiscordServiceMessageSenderTest.php | 4 ++-- 14 files changed, 32 insertions(+), 42 deletions(-) diff --git a/app/Discord/DiscordServiceInterface.php b/app/Discord/DiscordServiceInterface.php index e89ae5ff..03d65e4c 100644 --- a/app/Discord/DiscordServiceInterface.php +++ b/app/Discord/DiscordServiceInterface.php @@ -5,8 +5,8 @@ use App\Discord\Exception\DiscordServiceException; use App\Discord\Message\MessageInterface; -interface DiscordServiceInterface { - +interface DiscordServiceInterface +{ /** * Returns the remote message id, or throws an exception if the message could not be sent. * diff --git a/app/Discord/Exception/DiscordServiceException.php b/app/Discord/Exception/DiscordServiceException.php index b7a4fc53..766bad31 100644 --- a/app/Discord/Exception/DiscordServiceException.php +++ b/app/Discord/Exception/DiscordServiceException.php @@ -6,5 +6,4 @@ class DiscordServiceException extends RuntimeException { - } diff --git a/app/Discord/FlowMeasure/Content/FlowMeasureRecipientsFactory.php b/app/Discord/FlowMeasure/Content/FlowMeasureRecipientsFactory.php index 344cf3b1..5c3554d4 100644 --- a/app/Discord/FlowMeasure/Content/FlowMeasureRecipientsFactory.php +++ b/app/Discord/FlowMeasure/Content/FlowMeasureRecipientsFactory.php @@ -29,18 +29,18 @@ private function hasRecentlyBeenNotified(PendingMessageInterface $pendingMessage $measure = $pendingMessage->flowMeasure(); return $pendingMessage->type( ) === DiscordNotificationType::FLOW_MEASURE_ACTIVATED && $measure->notifiedDiscordNotifications->firstWhere( - fn(DivisionDiscordNotification $notification) => $notification->created_at > Carbon::now()->subHour() && + fn (DivisionDiscordNotification $notification) => $notification->created_at > Carbon::now()->subHour() && $notification->pivot->notified_as === $measure->identifier - ) !== null; + ) !== null; } private function ecfmpRecipients(PendingMessageInterface $pendingMessage): FlowMeasureRecipientsInterface { return new EcfmpInterestedParties( $pendingMessage->flowMeasure()->notifiedFlightInformationRegions - ->map(fn(FlightInformationRegion $flightInformationRegion) => $flightInformationRegion->discordTags) + ->map(fn (FlightInformationRegion $flightInformationRegion) => $flightInformationRegion->discordTags) ->flatten() - ->map(fn(DiscordTag $discordTag) => new Tag($discordTag)) + ->map(fn (DiscordTag $discordTag) => new Tag($discordTag)) ); } @@ -49,13 +49,13 @@ private function divisionRecipients(PendingMessageInterface $pendingMessage): Fl $recipients = DivisionDiscordWebhook::find($pendingMessage->webhook()->id()) ->flightInformationRegions ->filter( - fn(FlightInformationRegion $flightInformationRegion) => $pendingMessage + fn (FlightInformationRegion $flightInformationRegion) => $pendingMessage ->flowMeasure() ->notifiedFlightInformationRegions - ->firstWhere(fn(FlightInformationRegion $notifiedFir) => $notifiedFir->id === $flightInformationRegion->id) + ->firstWhere(fn (FlightInformationRegion $notifiedFir) => $notifiedFir->id === $flightInformationRegion->id) ) - ->filter(fn(FlightInformationRegion $flightInformationRegion) => !empty($flightInformationRegion->pivot->tag)) - ->map(fn(FlightInformationRegion $flightInformationRegion) => new Tag($flightInformationRegion->pivot)); + ->filter(fn (FlightInformationRegion $flightInformationRegion) => !empty($flightInformationRegion->pivot->tag)) + ->map(fn (FlightInformationRegion $flightInformationRegion) => new Tag($flightInformationRegion->pivot)); return $recipients->isEmpty() ? new NoRecipients() diff --git a/app/Discord/FlowMeasure/Helper/NotificationReissuer.php b/app/Discord/FlowMeasure/Helper/NotificationReissuer.php index 9a7b2e26..fbde93ac 100644 --- a/app/Discord/FlowMeasure/Helper/NotificationReissuer.php +++ b/app/Discord/FlowMeasure/Helper/NotificationReissuer.php @@ -34,14 +34,14 @@ public function isReissuedNotification(): bool ->get(); return $notificationsOfType->filter( - fn( - DivisionDiscordNotification $notification - ) => $notification->pivot->notified_as !== $this->measure->identifier + fn ( + DivisionDiscordNotification $notification + ) => $notification->pivot->notified_as !== $this->measure->identifier )->isNotEmpty() && $notificationsOfType->filter( - fn( + fn ( DivisionDiscordNotification $notification ) => $notification->pivot->notified_as === $this->measure->identifier - )->isEmpty(); + )->isEmpty(); } public function measure(): FlowMeasure diff --git a/app/Discord/FlowMeasure/Provider/PendingMessageInterface.php b/app/Discord/FlowMeasure/Provider/PendingMessageInterface.php index 95f09dac..26bb25c3 100644 --- a/app/Discord/FlowMeasure/Provider/PendingMessageInterface.php +++ b/app/Discord/FlowMeasure/Provider/PendingMessageInterface.php @@ -17,6 +17,6 @@ public function flowMeasure(): FlowMeasure; public function type(): DiscordNotificationType; public function reissue(): NotificationReissuerInterface; - + public function webhook(): ?WebhookInterface; } diff --git a/app/Discord/FlowMeasure/Webhook/Filter/ExpiredWebhookFilter.php b/app/Discord/FlowMeasure/Webhook/Filter/ExpiredWebhookFilter.php index 121b842f..9c7b1268 100644 --- a/app/Discord/FlowMeasure/Webhook/Filter/ExpiredWebhookFilter.php +++ b/app/Discord/FlowMeasure/Webhook/Filter/ExpiredWebhookFilter.php @@ -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) + ->reject(fn (FlowMeasure $activeMeasure) => $activeMeasure->id === $measure->id) ->count() < 3; } } diff --git a/app/Discord/Message/Embed/Embed.php b/app/Discord/Message/Embed/Embed.php index f1dbdce3..bd35853d 100644 --- a/app/Discord/Message/Embed/Embed.php +++ b/app/Discord/Message/Embed/Embed.php @@ -64,8 +64,7 @@ public function withField(FieldInterface $field, bool $condition = true): static public function withFields(Collection $fields): static { - $fields->each(function (FieldInterface $field) - { + $fields->each(function (FieldInterface $field) { $this->fields->add($field); }); @@ -100,7 +99,7 @@ public function toArray(): array } if ($this->fields->isNotEmpty()) { - $return['fields'] = $this->fields->map(fn(FieldInterface $field) => [ + $return['fields'] = $this->fields->map(fn (FieldInterface $field) => [ 'name' => $field->name(), 'value' => $field->value(), 'inline' => $field->inline(), @@ -120,8 +119,7 @@ public function toProtobuf(): DiscordEmbeds { return tap( new DiscordEmbeds(), - function (DiscordEmbeds $embed) - { + function (DiscordEmbeds $embed) { if (isset($this->title)) { $embed->setTitle($this->title->title()); } @@ -136,7 +134,7 @@ function (DiscordEmbeds $embed) if ($this->fields->isNotEmpty()) { $embed->setFields( - $this->fields->map(fn(FieldInterface $field) => new DiscordEmbedsFields([ + $this->fields->map(fn (FieldInterface $field) => new DiscordEmbedsFields([ 'name' => $field->name(), 'value' => $field->value(), 'inline' => $field->inline(), diff --git a/app/Models/FlowMeasure.php b/app/Models/FlowMeasure.php index 796474ca..4be5e547 100644 --- a/app/Models/FlowMeasure.php +++ b/app/Models/FlowMeasure.php @@ -180,7 +180,7 @@ public function filtersByType(FilterType $filterType): array return array_values( array_filter( $this->filters, - fn(array $filter) => FilterType::tryFrom($filter['type']) === $filterType + fn (array $filter) => FilterType::tryFrom($filter['type']) === $filterType ) ); } @@ -194,7 +194,7 @@ public function extraFilters(): array { return array_filter( $this->filters, - fn(array $filter) => !in_array( + fn (array $filter) => !in_array( FilterType::tryFrom($filter['type']), [FilterType::DEPARTURE_AIRPORTS, FilterType::ARRIVAL_AIRPORTS] ) @@ -215,8 +215,7 @@ public function scopeEndTimeWithinOneDay(Builder $builder): Builder public function status(): Attribute { - return new Attribute(function () - { + return new Attribute(function () { if ($this->trashed()) { return FlowMeasureStatus::DELETED; } diff --git a/app/Providers/DiscordServiceProvider.php b/app/Providers/DiscordServiceProvider.php index b288b377..57fe5158 100644 --- a/app/Providers/DiscordServiceProvider.php +++ b/app/Providers/DiscordServiceProvider.php @@ -3,7 +3,6 @@ namespace App\Providers; use App\Discord\Client\ClientFactory; -use App\Discord\DiscordServiceMessageSender; use App\Discord\DiscordWebhookInterface; use App\Discord\DiscordWebhookSender; use App\Discord\FlowMeasure\Message\FlowMeasureMessageFactory; @@ -42,7 +41,7 @@ public function register(): void $this->app->singleton(EcfmpWebhook::class); $this->app->singleton( DivisionWebhookSender::class, - fn() => new DivisionWebhookSender( + fn () => new DivisionWebhookSender( $this->flowMeasureMessageProviders(), $this->app->make(DiscordWebhookInterface::class) ) 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 index 8db87ff0..b4e3faa1 100644 --- a/database/migrations/2023_10_08_112953_rename_discord_notifications_tables.php +++ b/database/migrations/2023_10_08_112953_rename_discord_notifications_tables.php @@ -2,8 +2,7 @@ use Illuminate\Database\Migrations\Migration; -return new class extends Migration -{ +return new class () extends Migration { /** * Run the migrations. */ diff --git a/database/migrations/2023_10_08_122743_rename_column_on_division_discord_notification_flow_measures_table.php b/database/migrations/2023_10_08_122743_rename_column_on_division_discord_notification_flow_measures_table.php index 956ed9f2..b86cbd6e 100644 --- a/database/migrations/2023_10_08_122743_rename_column_on_division_discord_notification_flow_measures_table.php +++ b/database/migrations/2023_10_08_122743_rename_column_on_division_discord_notification_flow_measures_table.php @@ -3,8 +3,7 @@ use Illuminate\Database\Migrations\Migration; use Illuminate\Support\Facades\DB; -return new class extends Migration -{ +return new class () extends Migration { /** * Run the migrations. */ diff --git a/database/migrations/2023_10_08_123536_create_discord_notifications_table.php b/database/migrations/2023_10_08_123536_create_discord_notifications_table.php index b41871dc..26ea4bfa 100644 --- a/database/migrations/2023_10_08_123536_create_discord_notifications_table.php +++ b/database/migrations/2023_10_08_123536_create_discord_notifications_table.php @@ -4,8 +4,7 @@ use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; -return new class extends Migration -{ +return new class () extends Migration { /** * Run the migrations. */ 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 index 17aadfc0..e74f4047 100644 --- 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 @@ -4,15 +4,13 @@ use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; -return new class extends Migration -{ +return new class () extends Migration { /** * Run the migrations. */ public function up(): void { - Schema::create('discord_notification_flow_measure', function (Blueprint $table) - { + Schema::create('discord_notification_flow_measure', function (Blueprint $table) { $table->id(); $table->foreignId('discord_notification_id') ->constrained('discord_notifications', indexName: 'discord_notification_id') diff --git a/tests/Discord/DiscordServiceMessageSenderTest.php b/tests/Discord/DiscordServiceMessageSenderTest.php index 716c8393..c66ce7a8 100644 --- a/tests/Discord/DiscordServiceMessageSenderTest.php +++ b/tests/Discord/DiscordServiceMessageSenderTest.php @@ -66,7 +66,7 @@ public function testItSendsAMessageAndReturnsTheId() $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' && + fn (CreateRequest $request) => $request->getContent() === 'content' && count($request->getEmbeds()) === 1 && $request->getEmbeds()[0] == $this->discordEmbeds ), [ @@ -88,7 +88,7 @@ public function testItThrowsAnExceptionIfStatusIsNotOk() $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' && + fn (CreateRequest $request) => $request->getContent() === 'content' && count($request->getEmbeds()) === 1 && $request->getEmbeds()[0] == $this->discordEmbeds ), [ From 19605bb2abe59755b2745ab44523a1d202282c06 Mon Sep 17 00:00:00 2001 From: Andy Ford Date: Fri, 13 Oct 2023 17:23:42 +0100 Subject: [PATCH 15/34] deps: bump protobuf --- protobuf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protobuf b/protobuf index a79f7171..27e7dd91 160000 --- a/protobuf +++ b/protobuf @@ -1 +1 @@ -Subproject commit a79f7171c726fb68eda294fc032fe1c6987b4051 +Subproject commit 27e7dd910bb44902d4cf90d91a6e115832cfa0a5 From 1f94e06795bfcb1ea6fdc2bc5cb3d52c25070197 Mon Sep 17 00:00:00 2001 From: Andy Ford Date: Fri, 13 Oct 2023 18:14:53 +0100 Subject: [PATCH 16/34] refactor: rename model methods --- .../Content/FlowMeasureRecipientsFactory.php | 2 +- .../Helper/NotificationReissuer.php | 3 ++- .../FlowMeasure/Provider/EcfmpProvider.php | 15 +++++++++++ .../Webhook/Filter/ActivatedWebhookFilter.php | 2 +- .../Webhook/Filter/ExpiredWebhookFilter.php | 2 +- .../Webhook/Filter/NotifiedWebhookFilter.php | 4 +-- .../Webhook/Filter/WithdrawnWebhookFilter.php | 4 +-- app/Models/FlowMeasure.php | 26 +++++++++---------- app/Providers/DiscordServiceProvider.php | 3 ++- 9 files changed, 39 insertions(+), 22 deletions(-) create mode 100644 app/Discord/FlowMeasure/Provider/EcfmpProvider.php diff --git a/app/Discord/FlowMeasure/Content/FlowMeasureRecipientsFactory.php b/app/Discord/FlowMeasure/Content/FlowMeasureRecipientsFactory.php index 5c3554d4..dc0c3d9a 100644 --- a/app/Discord/FlowMeasure/Content/FlowMeasureRecipientsFactory.php +++ b/app/Discord/FlowMeasure/Content/FlowMeasureRecipientsFactory.php @@ -28,7 +28,7 @@ private function hasRecentlyBeenNotified(PendingMessageInterface $pendingMessage { $measure = $pendingMessage->flowMeasure(); return $pendingMessage->type( - ) === DiscordNotificationType::FLOW_MEASURE_ACTIVATED && $measure->notifiedDiscordNotifications->firstWhere( + ) === DiscordNotificationType::FLOW_MEASURE_ACTIVATED && $measure->notifiedDivisionNotifications->firstWhere( fn (DivisionDiscordNotification $notification) => $notification->created_at > Carbon::now()->subHour() && $notification->pivot->notified_as === $measure->identifier ) !== null; diff --git a/app/Discord/FlowMeasure/Helper/NotificationReissuer.php b/app/Discord/FlowMeasure/Helper/NotificationReissuer.php index fbde93ac..bf51bbee 100644 --- a/app/Discord/FlowMeasure/Helper/NotificationReissuer.php +++ b/app/Discord/FlowMeasure/Helper/NotificationReissuer.php @@ -20,6 +20,7 @@ public function __construct(FlowMeasure $measure, DiscordNotificationType $type, $this->webhook = $webhook; } + // TODO: Update this public function isReissuedNotification(): bool { if ( @@ -29,7 +30,7 @@ public function isReissuedNotification(): bool return false; } - $notificationsOfType = $this->measure->activatedAndNotifiedNotifications() + $notificationsOfType = $this->measure->activatedAndNotifiedDivisionNotifications() ->where('division_discord_webhook_id', $this->webhook->id()) ->get(); diff --git a/app/Discord/FlowMeasure/Provider/EcfmpProvider.php b/app/Discord/FlowMeasure/Provider/EcfmpProvider.php new file mode 100644 index 00000000..a0a48bdf --- /dev/null +++ b/app/Discord/FlowMeasure/Provider/EcfmpProvider.php @@ -0,0 +1,15 @@ +existingNotificationDoesntExist( - $flowMeasure->activatedDiscordNotifications() + $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 9c7b1268..46659a4b 100644 --- a/app/Discord/FlowMeasure/Webhook/Filter/ExpiredWebhookFilter.php +++ b/app/Discord/FlowMeasure/Webhook/Filter/ExpiredWebhookFilter.php @@ -37,7 +37,7 @@ public function shouldUseWebhook(FlowMeasure $flowMeasure, WebhookInterface $web } return $this->existingNotificationDoesntExist( - $flowMeasure->withdrawnAndExpiredDiscordNotifications(), + $flowMeasure->withdrawnAndExpiredDivisionNotifications(), $webhook ); } diff --git a/app/Discord/FlowMeasure/Webhook/Filter/NotifiedWebhookFilter.php b/app/Discord/FlowMeasure/Webhook/Filter/NotifiedWebhookFilter.php index f2cb24d5..01bf4144 100644 --- a/app/Discord/FlowMeasure/Webhook/Filter/NotifiedWebhookFilter.php +++ b/app/Discord/FlowMeasure/Webhook/Filter/NotifiedWebhookFilter.php @@ -20,7 +20,7 @@ public function shouldUseWebhook(FlowMeasure $flowMeasure, WebhookInterface $web private function notYetNotified(FlowMeasure $flowMeasure, WebhookInterface $webhook): bool { return $this->existingNotificationDoesntExist( - $flowMeasure->notifiedDiscordNotifications() + $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/Models/FlowMeasure.php b/app/Models/FlowMeasure.php index 4be5e547..07ded6db 100644 --- a/app/Models/FlowMeasure.php +++ b/app/Models/FlowMeasure.php @@ -123,29 +123,29 @@ public function divisionDiscordNotifications(): BelongsToMany ->withTimestamps(); } - public function notifiedDiscordNotifications(): BelongsToMany + public function notifiedDivisionNotifications(): BelongsToMany { - return $this->notificationsOfType([DiscordNotificationTypeEnum::FLOW_MEASURE_NOTIFIED]); + return $this->divisionNotificationsOfType([DiscordNotificationTypeEnum::FLOW_MEASURE_NOTIFIED]); } - public function activatedDiscordNotifications(): BelongsToMany + public function activatedDivisionNotifications(): BelongsToMany { - return $this->notificationsOfType([DiscordNotificationTypeEnum::FLOW_MEASURE_ACTIVATED]); + return $this->divisionNotificationsOfType([DiscordNotificationTypeEnum::FLOW_MEASURE_ACTIVATED]); } - public function withdrawnDiscordNotifications(): BelongsToMany + public function withdrawnDivisionNotifications(): BelongsToMany { - return $this->notificationsOfType([DiscordNotificationTypeEnum::FLOW_MEASURE_WITHDRAWN]); + return $this->divisionNotificationsOfType([DiscordNotificationTypeEnum::FLOW_MEASURE_WITHDRAWN]); } - public function expiredDiscordNotifications(): BelongsToMany + public function expiredDivisionNotifications(): BelongsToMany { - return $this->notificationsOfType([DiscordNotificationTypeEnum::FLOW_MEASURE_EXPIRED]); + return $this->divisionNotificationsOfType([DiscordNotificationTypeEnum::FLOW_MEASURE_EXPIRED]); } - public function withdrawnAndExpiredDiscordNotifications(): BelongsToMany + public function withdrawnAndExpiredDivisionNotifications(): BelongsToMany { - return $this->notificationsOfType( + return $this->divisionNotificationsOfType( [ DiscordNotificationTypeEnum::FLOW_MEASURE_WITHDRAWN, DiscordNotificationTypeEnum::FLOW_MEASURE_EXPIRED, @@ -153,9 +153,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,7 +163,7 @@ public function activatedAndNotifiedNotifications(): BelongsToMany ); } - private function notificationsOfType(array $types): BelongsToMany + private function divisionNotificationsOfType(array $types): BelongsToMany { return $this->divisionDiscordNotifications() ->wherePivotIn( diff --git a/app/Providers/DiscordServiceProvider.php b/app/Providers/DiscordServiceProvider.php index 57fe5158..baf8e772 100644 --- a/app/Providers/DiscordServiceProvider.php +++ b/app/Providers/DiscordServiceProvider.php @@ -8,6 +8,7 @@ use App\Discord\FlowMeasure\Message\FlowMeasureMessageFactory; use App\Discord\FlowMeasure\Message\MessageGenerator; use App\Discord\FlowMeasure\Message\MessageGeneratorInterface; +use App\Discord\FlowMeasure\Provider\DivisionWebhookMessageProvider; use App\Discord\FlowMeasure\Provider\MessageProvider; use App\Discord\FlowMeasure\Webhook\Filter\ActivatedWebhookFilter; use App\Discord\FlowMeasure\Webhook\Filter\ExpiredWebhookFilter; @@ -69,7 +70,7 @@ private function makeMessageProvider( FilterInterface $filter ): MessageGeneratorInterface { return new MessageGenerator( - new MessageProvider( + new DivisionWebhookMessageProvider( $repository, $this->app->make( WebhookMapper::class, From ae21aeeb7cbaee6b9ba8cb8c3c4aa7e78324ec14 Mon Sep 17 00:00:00 2001 From: Andy Ford Date: Fri, 13 Oct 2023 18:29:36 +0100 Subject: [PATCH 17/34] fix: schema --- ...iscord_notification_flow_measure_table.php | 3 ++ database/schema/mysql-schema.sql | 32 +++++++++---------- 2 files changed, 19 insertions(+), 16 deletions(-) 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 index e74f4047..076168b8 100644 --- 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 @@ -12,6 +12,9 @@ public function up(): void { Schema::create('discord_notification_flow_measure', function (Blueprint $table) { $table->id(); + $table->string('notified_as') + ->index() + ->comment('The identifier of the flow measure at the point of notification'); $table->foreignId('discord_notification_id') ->constrained('discord_notifications', indexName: 'discord_notification_id') ->cascadeOnDelete(); diff --git a/database/schema/mysql-schema.sql b/database/schema/mysql-schema.sql index 7cc0c666..cbca8ab7 100644 --- a/database/schema/mysql-schema.sql +++ b/database/schema/mysql-schema.sql @@ -219,13 +219,13 @@ LOCK TABLES `discord_tags` WRITE; UNLOCK TABLES; -- --- Table structure for table `division_discord_notification_flow_measure` +-- Table structure for table `discord_notification_flow_measure` -- -DROP TABLE IF EXISTS `division_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 `division_discord_notification_flow_measure` ( +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, @@ -237,29 +237,29 @@ CREATE TABLE `division_discord_notification_flow_measure` ( 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 `division_discord_notifications` (`id`) ON DELETE CASCADE, + 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 `division_discord_notification_flow_measure` +-- Dumping data for table `discord_notification_flow_measure` -- -LOCK TABLES `division_discord_notification_flow_measure` WRITE; -/*!40000 ALTER TABLE `division_discord_notification_flow_measure` DISABLE KEYS */; -/*!40000 ALTER TABLE `division_discord_notification_flow_measure` ENABLE KEYS */; +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 `division_discord_notifications` +-- Table structure for table `discord_notifications` -- -DROP TABLE IF EXISTS `division_discord_notifications`; +DROP TABLE IF EXISTS `discord_notifications`; /*!40101 SET @saved_cs_client = @@character_set_client */; /*!50503 SET character_set_client = utf8mb4 */; -CREATE TABLE `division_discord_notifications` ( +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, @@ -273,12 +273,12 @@ CREATE TABLE `division_discord_notifications` ( /*!40101 SET character_set_client = @saved_cs_client */; -- --- Dumping data for table `division_discord_notifications` +-- Dumping data for table `discord_notifications` -- -LOCK TABLES `division_discord_notifications` WRITE; -/*!40000 ALTER TABLE `division_discord_notifications` DISABLE KEYS */; -/*!40000 ALTER TABLE `division_discord_notifications` ENABLE KEYS */; +LOCK TABLES `discord_notifications` WRITE; +/*!40000 ALTER TABLE `discord_notifications` DISABLE KEYS */; +/*!40000 ALTER TABLE `discord_notifications` ENABLE KEYS */; UNLOCK TABLES; -- @@ -586,7 +586,7 @@ CREATE 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),(42,'2023_10_08_112953_rename_discord_notifications_tables',1); +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; From 15043b7ff7dd9b3b9b16b65677584203e4756a85 Mon Sep 17 00:00:00 2001 From: Andy Ford Date: Fri, 13 Oct 2023 20:03:55 +0100 Subject: [PATCH 18/34] refactor: start queries ecfmp webhook --- app/Models/DiscordNotification.php | 25 ++++ app/Models/FlowMeasure.php | 49 +++++++ .../ActiveRepository.php | 7 + .../ExpiredRepository.php | 13 +- .../NotifiedRepository.php | 9 ++ .../RepositoryInterface.php | 5 + .../WithdrawnRepository.php | 21 +++ .../factories/DiscordNotificationFactory.php | 23 ++++ database/factories/FlowMeasureFactory.php | 8 ++ ...536_create_discord_notifications_table.php | 5 +- ...iscord_notification_flow_measure_table.php | 17 ++- .../ActiveRepositoryTest.php | 77 ++++++++++- .../ExpiredRepositoryTest.php | 10 ++ .../NotifiedRepositoryTest.php | 75 ++++++++++- .../WithdrawnRepositoryTest.php | 124 +++++++++++++++++- 15 files changed, 454 insertions(+), 14 deletions(-) create mode 100644 app/Models/DiscordNotification.php create mode 100644 database/factories/DiscordNotificationFactory.php diff --git a/app/Models/DiscordNotification.php b/app/Models/DiscordNotification.php new file mode 100644 index 00000000..8328a434 --- /dev/null +++ b/app/Models/DiscordNotification.php @@ -0,0 +1,25 @@ +belongsToMany(FlowMeasure::class) + ->withPivot(['notified_as', 'discord_notification_type_id']) + ->withTimestamps(); + } +} diff --git a/app/Models/FlowMeasure.php b/app/Models/FlowMeasure.php index 07ded6db..d257c3a5 100644 --- a/app/Models/FlowMeasure.php +++ b/app/Models/FlowMeasure.php @@ -123,6 +123,13 @@ public function divisionDiscordNotifications(): BelongsToMany ->withTimestamps(); } + public function discordNotifications(): BelongsToMany + { + return $this->belongsToMany(DiscordNotification::class) + ->withPivot(['discord_notification_type_id', 'notified_as']) + ->withTimestamps(); + } + public function notifiedDivisionNotifications(): BelongsToMany { return $this->divisionNotificationsOfType([DiscordNotificationTypeEnum::FLOW_MEASURE_NOTIFIED]); @@ -163,6 +170,48 @@ public function activatedAndNotifiedDivisionNotifications(): 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->divisionDiscordNotifications() diff --git a/app/Repository/FlowMeasureNotification/ActiveRepository.php b/app/Repository/FlowMeasureNotification/ActiveRepository.php index 5266b20a..3f76ae08 100644 --- a/app/Repository/FlowMeasureNotification/ActiveRepository.php +++ b/app/Repository/FlowMeasureNotification/ActiveRepository.php @@ -8,6 +8,13 @@ class ActiveRepository implements RepositoryInterface { + public function flowMeasuresToBeSentToEcfmp(): Collection + { + return FlowMeasure::active() + ->withoutEcfmpNotificationOfTypeForIdentifier($this->notificationType()) + ->get(); + } + public function flowMeasuresForNotification(): Collection { return FlowMeasure::with('divisionDiscordNotifications') diff --git a/app/Repository/FlowMeasureNotification/ExpiredRepository.php b/app/Repository/FlowMeasureNotification/ExpiredRepository.php index b787bc71..e119ebf3 100644 --- a/app/Repository/FlowMeasureNotification/ExpiredRepository.php +++ b/app/Repository/FlowMeasureNotification/ExpiredRepository.php @@ -8,11 +8,20 @@ class ExpiredRepository implements RepositoryInterface { + public function flowMeasuresToBeSentToEcfmp(): Collection + { + return FlowMeasure::expiredRecently() + ->withoutEcfmpNotificationOfTypes([ + DiscordNotificationType::FLOW_MEASURE_EXPIRED, + DiscordNotificationType::FLOW_MEASURE_WITHDRAWN, + ]) + ->get(); + } + public function flowMeasuresForNotification(): Collection { return FlowMeasure::with('divisionDiscordNotifications') - ->expiredRecently() - ->get(); + ->expiredRecently(); } public function notificationType(): DiscordNotificationType diff --git a/app/Repository/FlowMeasureNotification/NotifiedRepository.php b/app/Repository/FlowMeasureNotification/NotifiedRepository.php index 004a421f..ca9b00de 100644 --- a/app/Repository/FlowMeasureNotification/NotifiedRepository.php +++ b/app/Repository/FlowMeasureNotification/NotifiedRepository.php @@ -9,6 +9,15 @@ class NotifiedRepository implements RepositoryInterface { + public function flowMeasuresToBeSentToEcfmp(): Collection + { + return FlowMeasure::where('start_time', '<', Carbon::now()->addDay()) + ->where('start_time', '>', Carbon::now()) + ->withoutEcfmpNotificationOfTypeForIdentifier($this->notificationType()) + ->withoutEcfmpNotificationOfType(DiscordNotificationType::FLOW_MEASURE_ACTIVATED) + ->get(); + } + public function flowMeasuresForNotification(): Collection { return FlowMeasure::with('divisionDiscordNotifications') 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 0b0d1b83..0e2e28b7 100644 --- a/app/Repository/FlowMeasureNotification/WithdrawnRepository.php +++ b/app/Repository/FlowMeasureNotification/WithdrawnRepository.php @@ -10,6 +10,14 @@ class WithdrawnRepository implements RepositoryInterface { + public function flowMeasuresToBeSentToEcfmp(): Collection + { + return $this->baseQueryForEcfmp()->notified() + ->union($this->baseQueryForEcfmp()->active()) + ->orderBy('id') + ->get(); + } + public function flowMeasuresForNotification(): Collection { return $this->baseQuery()->notified() @@ -18,6 +26,19 @@ 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('divisionDiscordNotifications') diff --git a/database/factories/DiscordNotificationFactory.php b/database/factories/DiscordNotificationFactory.php new file mode 100644 index 00000000..1d3eb03a --- /dev/null +++ b/database/factories/DiscordNotificationFactory.php @@ -0,0 +1,23 @@ + + */ +class DiscordNotificationFactory extends Factory +{ + /** + * Define the model's default state. + * + * @return array + */ + public function definition() + { + return [ + 'remote_id' => $this->faker->uuid, + ]; + } +} diff --git a/database/factories/FlowMeasureFactory.php b/database/factories/FlowMeasureFactory.php index 0942e40b..91338d11 100644 --- a/database/factories/FlowMeasureFactory.php +++ b/database/factories/FlowMeasureFactory.php @@ -102,6 +102,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_123536_create_discord_notifications_table.php b/database/migrations/2023_10_08_123536_create_discord_notifications_table.php index 26ea4bfa..1a0e3a64 100644 --- a/database/migrations/2023_10_08_123536_create_discord_notifications_table.php +++ b/database/migrations/2023_10_08_123536_create_discord_notifications_table.php @@ -12,8 +12,9 @@ public function up(): void { Schema::create('discord_notifications', function (Blueprint $table) { $table->id(); - $table->uuid('remote_id')->unique(); - $table->timestamps(); + $table->uuid('remote_id') + ->unique(); + $table->timestamp('created_at'); }); } 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 index 076168b8..6bf85cc0 100644 --- 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 @@ -1,5 +1,6 @@ id(); - $table->string('notified_as') - ->index() - ->comment('The identifier of the flow measure at the point of notification'); + + $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') + ->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(); }); } diff --git a/tests/Repository/FlowMeasureNotification/ActiveRepositoryTest.php b/tests/Repository/FlowMeasureNotification/ActiveRepositoryTest.php index b15a8ed8..a2083dd3 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,75 @@ 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('id')->toArray() + ); + } } diff --git a/tests/Repository/FlowMeasureNotification/ExpiredRepositoryTest.php b/tests/Repository/FlowMeasureNotification/ExpiredRepositoryTest.php index ba56990e..2f94fe81 100644 --- a/tests/Repository/FlowMeasureNotification/ExpiredRepositoryTest.php +++ b/tests/Repository/FlowMeasureNotification/ExpiredRepositoryTest.php @@ -63,4 +63,14 @@ public function testItIgnoresActiveFlowMeasures() FlowMeasure::factory()->count(3)->create(); $this->assertEmpty($this->repository->flowMeasuresForNotification()); } + + public function testItReturnsQueryForExtension() + { + $measures = FlowMeasure::factory()->finishedRecently()->count(3)->create(); + + $this->assertEquals( + $measures->pluck('id')->toArray(), + $this->repository->flowMeasureQuery()->get()->pluck('id')->toArray() + ); + } } diff --git a/tests/Repository/FlowMeasureNotification/NotifiedRepositoryTest.php b/tests/Repository/FlowMeasureNotification/NotifiedRepositoryTest.php index 35255f91..4bdeb458 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,73 @@ 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('id')->toArray() + ); + } } diff --git a/tests/Repository/FlowMeasureNotification/WithdrawnRepositoryTest.php b/tests/Repository/FlowMeasureNotification/WithdrawnRepositoryTest.php index 81311b61..490de257 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,121 @@ 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('id')->toArray() + ); + } } From d9332a9d9be46764c416597f30bb4e8602cb7939 Mon Sep 17 00:00:00 2001 From: Andy Ford Date: Sun, 15 Oct 2023 12:10:27 +0100 Subject: [PATCH 19/34] repositories for loading flow measures for sending to ecfmp --- .../Pages/CreateFlowMeasure.php | 2 + .../Pages/EditFlowMeasure.php | 4 + .../FlowMeasureIdentifierGenerator.php | 25 ++- app/Models/FlowMeasure.php | 3 + .../ExpiredRepository.php | 17 +- database/factories/FlowMeasureFactory.php | 5 +- ...204_add_columns_to_flow_measures_table.php | 34 ++++ ..._10_14_150428_update_flow_measure_data.php | 28 +++ .../Filament/FlowMeasureResourceTest.php | 66 ++++++- .../FlowMeasureIdentifierGeneratorTest.php | 23 +++ .../ExpiredRepositoryTest.php | 186 +++++++++++++++++- tests/TestCase.php | 1 + 12 files changed, 381 insertions(+), 13 deletions(-) create mode 100644 database/migrations/2023_10_14_150204_add_columns_to_flow_measures_table.php create mode 100644 database/migrations/2023_10_14_150428_update_flow_measure_data.php 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/Models/FlowMeasure.php b/app/Models/FlowMeasure.php index d257c3a5..0bd174c4 100644 --- a/app/Models/FlowMeasure.php +++ b/app/Models/FlowMeasure.php @@ -23,6 +23,8 @@ class FlowMeasure extends Model protected $fillable = [ 'identifier', + 'canonical_identifier', + 'revision_number', 'user_id', 'flight_information_region_id', 'event_id', @@ -36,6 +38,7 @@ class FlowMeasure extends Model ]; protected $casts = [ + 'revision_number' => 'integer', 'mandatory_route' => 'array', 'filters' => 'array', 'start_time' => 'datetime', diff --git a/app/Repository/FlowMeasureNotification/ExpiredRepository.php b/app/Repository/FlowMeasureNotification/ExpiredRepository.php index e119ebf3..2c152f4c 100644 --- a/app/Repository/FlowMeasureNotification/ExpiredRepository.php +++ b/app/Repository/FlowMeasureNotification/ExpiredRepository.php @@ -3,14 +3,28 @@ 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, @@ -21,7 +35,8 @@ public function flowMeasuresToBeSentToEcfmp(): Collection public function flowMeasuresForNotification(): Collection { return FlowMeasure::with('divisionDiscordNotifications') - ->expiredRecently(); + ->expiredRecently() + ->get(); } public function notificationType(): DiscordNotificationType diff --git a/database/factories/FlowMeasureFactory.php b/database/factories/FlowMeasureFactory.php index 91338d11..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, 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..5b6fb535 --- /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..309f71a2 --- /dev/null +++ b/database/migrations/2023_10_14_150428_update_flow_measure_data.php @@ -0,0 +1,28 @@ +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/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/Repository/FlowMeasureNotification/ExpiredRepositoryTest.php b/tests/Repository/FlowMeasureNotification/ExpiredRepositoryTest.php index 2f94fe81..2e151e4b 100644 --- a/tests/Repository/FlowMeasureNotification/ExpiredRepositoryTest.php +++ b/tests/Repository/FlowMeasureNotification/ExpiredRepositoryTest.php @@ -2,9 +2,14 @@ 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\Facades\DB; +use Illuminate\Support\Str; use Tests\TestCase; class ExpiredRepositoryTest extends TestCase @@ -14,13 +19,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() ); } @@ -64,13 +69,180 @@ public function testItIgnoresActiveFlowMeasures() $this->assertEmpty($this->repository->flowMeasuresForNotification()); } - public function testItReturnsQueryForExtension() + public function testItReturnsEmptyForEcfmpMeasuresIfMoreThanFiveNotificationsSentInLastTwoHours() { - $measures = FlowMeasure::factory()->finishedRecently()->count(3)->create(); + 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( - $measures->pluck('id')->toArray(), - $this->repository->flowMeasureQuery()->get()->pluck('id')->toArray() + [$flowMeasure1->id, $flowMeasure2->id], + $this->repository->flowMeasuresToBeSentToEcfmp()->pluck('id')->toArray() ); } } diff --git a/tests/TestCase.php b/tests/TestCase.php index ccacace9..fe1f8016 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -13,6 +13,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(); From 6a1f6c2332874a3817def710cb4976042d4a232c Mon Sep 17 00:00:00 2001 From: Andy Ford Date: Mon, 16 Oct 2023 17:59:09 +0100 Subject: [PATCH 20/34] feat: discord messages sent by services --- .env.example | 4 +- .../Commands/SendEcfmpDiscordMessages.php | 38 +++ app/Discord/DiscordServiceInterface.php | 4 +- app/Discord/DiscordServiceMessageSender.php | 15 +- .../Content/FlowMeasureRecipientsFactory.php | 31 ++- .../FlowMeasure/Embed/ActivatedEmbeds.php | 2 +- .../FlowMeasure/Embed/NotifiedEmbeds.php | 2 +- .../EcfmpFlowMeasureMessageGenerator.php | 37 +++ .../Helper/EcfmpNotificationReissuer.php | 25 ++ .../Message/EcfmpFlowMeasureMessage.php | 37 +++ .../Message/FlowMeasureMessageFactory.php | 13 +- .../FlowMeasure/Provider/EcfmpProvider.php | 15 -- ...e.php => PendingDiscordWebhookMessage.php} | 11 +- .../Provider/PendingEcfmpMessage.php | 43 +++ .../Provider/PendingMessageInterface.php | 3 +- .../PendingWebhookMessageInterface.php | 10 + .../Sender/EcfmpFlowMeasureSender.php | 66 +++++ app/Discord/Message/EcfmpMessageInterface.php | 26 ++ app/Jobs/SendDiscordNotifications.php | 6 +- app/Models/FlowMeasure.php | 26 +- app/Providers/DiscordServiceProvider.php | 23 +- .../ActiveRepository.php | 28 +- .../ExpiredRepository.php | 8 +- .../FlowMeasureForNotification.php | 14 + .../NotifiedRepository.php | 28 +- .../WithdrawnRepository.php | 8 +- composer.json | 2 +- config/discord.php | 2 + routes/web.php | 10 + .../Commands/SendEcfmpDiscordMessagesTest.php | 39 +++ .../DiscordServiceMessageSenderTest.php | 12 +- .../FlowMeasureRecipientsFactoryTest.php | 248 ++++++++++++------ .../FlowMeasure/Embed/ActivatedEmbedsTest.php | 14 +- .../FlowMeasure/Embed/NotifiedEmbedsTest.php | 14 +- .../EcfmpFlowMeasureMessageGeneratorTest.php | 65 +++++ .../Helper/EcfmpNotificationReissuerTest.php | 35 +++ .../Message/FlowMeasureMessageFactoryTest.php | 27 +- ...p => PendingDiscordWebhookMessageTest.php} | 14 +- .../Provider/PendingEcfmpMessageTest.php | 47 ++++ .../Sender/EcfmpFlowMeasureSenderTest.php | 88 +++++++ tests/Jobs/SendDiscordNotificationsTest.php | 9 + .../ActiveRepositoryTest.php | 76 +++++- .../ExpiredRepositoryTest.php | 25 +- .../NotifiedRepositoryTest.php | 34 ++- .../WithdrawnRepositoryTest.php | 26 +- 45 files changed, 1135 insertions(+), 175 deletions(-) create mode 100644 app/Console/Commands/SendEcfmpDiscordMessages.php create mode 100644 app/Discord/FlowMeasure/Generator/EcfmpFlowMeasureMessageGenerator.php create mode 100644 app/Discord/FlowMeasure/Helper/EcfmpNotificationReissuer.php create mode 100644 app/Discord/FlowMeasure/Message/EcfmpFlowMeasureMessage.php delete mode 100644 app/Discord/FlowMeasure/Provider/EcfmpProvider.php rename app/Discord/FlowMeasure/Provider/{PendingDiscordMessage.php => PendingDiscordWebhookMessage.php} (82%) create mode 100644 app/Discord/FlowMeasure/Provider/PendingEcfmpMessage.php create mode 100644 app/Discord/FlowMeasure/Provider/PendingWebhookMessageInterface.php create mode 100644 app/Discord/FlowMeasure/Sender/EcfmpFlowMeasureSender.php create mode 100644 app/Discord/Message/EcfmpMessageInterface.php create mode 100644 app/Repository/FlowMeasureNotification/FlowMeasureForNotification.php create mode 100644 tests/Console/Commands/SendEcfmpDiscordMessagesTest.php create mode 100644 tests/Discord/FlowMeasure/Generator/EcfmpFlowMeasureMessageGeneratorTest.php create mode 100644 tests/Discord/FlowMeasure/Helper/EcfmpNotificationReissuerTest.php rename tests/Discord/FlowMeasure/Provider/{PendingDiscordMessageTest.php => PendingDiscordWebhookMessageTest.php} (73%) create mode 100644 tests/Discord/FlowMeasure/Provider/PendingEcfmpMessageTest.php create mode 100644 tests/Discord/FlowMeasure/Sender/EcfmpFlowMeasureSenderTest.php diff --git a/.env.example b/.env.example index a92082f9..5ca7fbe7 100644 --- a/.env.example +++ b/.env.example @@ -62,11 +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_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/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/DiscordServiceInterface.php b/app/Discord/DiscordServiceInterface.php index 03d65e4c..ef750015 100644 --- a/app/Discord/DiscordServiceInterface.php +++ b/app/Discord/DiscordServiceInterface.php @@ -3,7 +3,7 @@ namespace App\Discord; use App\Discord\Exception\DiscordServiceException; -use App\Discord\Message\MessageInterface; +use App\Discord\Message\EcfmpMessageInterface; interface DiscordServiceInterface { @@ -12,5 +12,5 @@ interface DiscordServiceInterface * * @throws DiscordServiceException */ - public function sendMessage(string $clientRequestId, MessageInterface $message): string; + public function sendMessage(string $clientRequestId, EcfmpMessageInterface $message): string; } diff --git a/app/Discord/DiscordServiceMessageSender.php b/app/Discord/DiscordServiceMessageSender.php index e321ff1b..c78fa955 100644 --- a/app/Discord/DiscordServiceMessageSender.php +++ b/app/Discord/DiscordServiceMessageSender.php @@ -4,7 +4,7 @@ use App\Discord\Client\ClientFactoryInterface; use App\Discord\Exception\DiscordServiceException; -use App\Discord\Message\MessageInterface; +use App\Discord\Message\EcfmpMessageInterface; use Ecfmp_discord\CreateRequest; use Log; @@ -19,7 +19,7 @@ public function __construct(ClientFactoryInterface $discordClientFactory) $this->discordClientFactory = $discordClientFactory; } - public function sendMessage(string $clientRequestId, MessageInterface $message): string + public function sendMessage(string $clientRequestId, EcfmpMessageInterface $message): string { $client = $this->discordClientFactory->create(); @@ -36,6 +36,7 @@ public function sendMessage(string $clientRequestId, MessageInterface $message): [$response, $status] = $client->Create( new CreateRequest( [ + 'channel' => $message->channel(), 'content' => $message->content(), 'embeds' => $message->embeds()->toProtobuf(), ] @@ -55,16 +56,6 @@ public function sendMessage(string $clientRequestId, MessageInterface $message): throw new DiscordServiceException('Discord grpc call failed'); } - // TODO: - // Discord message sender can remain the same... - // The "new" sender can remain the same, but of course we give it an "update" method - - // The class "Sender" now only handles division webhooks - - // We create a new set of filters that filter out messages to send based on its ECFMP bot status - // We create a new "associator" type for ECFMP messages - // We create a new sender class for ECFMP messages (for first time sending) - // Updates, we handle outside of the normal flow (e.g. a job) return $response->getId(); } } diff --git a/app/Discord/FlowMeasure/Content/FlowMeasureRecipientsFactory.php b/app/Discord/FlowMeasure/Content/FlowMeasureRecipientsFactory.php index dc0c3d9a..0c277092 100644 --- a/app/Discord/FlowMeasure/Content/FlowMeasureRecipientsFactory.php +++ b/app/Discord/FlowMeasure/Content/FlowMeasureRecipientsFactory.php @@ -3,8 +3,10 @@ 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; @@ -13,18 +15,25 @@ 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() === null - ? $this->ecfmpRecipients($pendingMessage) - : $this->divisionRecipients($pendingMessage); + return $this->ecfmpRecipients($pendingMessage); } - private function hasRecentlyBeenNotified(PendingMessageInterface $pendingMessage): bool + private function hasRecentlyBeenNotifiedToWebhook(PendingWebhookMessageInterface $pendingMessage): bool { $measure = $pendingMessage->flowMeasure(); return $pendingMessage->type( @@ -34,6 +43,16 @@ private function hasRecentlyBeenNotified(PendingMessageInterface $pendingMessage ) !== null; } + private function hasRecentlyBeenNotified(PendingMessageInterface $pendingMessage): bool + { + $measure = $pendingMessage->flowMeasure(); + return $pendingMessage->type( + ) === DiscordNotificationType::FLOW_MEASURE_ACTIVATED && $measure->notifiedEcfmpNotifications->firstWhere( + fn (DiscordNotification $notification) => $notification->created_at > Carbon::now()->subHour() && + $notification->pivot->notified_as === $measure->identifier + ) !== null; + } + private function ecfmpRecipients(PendingMessageInterface $pendingMessage): FlowMeasureRecipientsInterface { return new EcfmpInterestedParties( @@ -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 9950e4e5..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())) + ->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..cc971e28 --- /dev/null +++ b/app/Discord/FlowMeasure/Helper/EcfmpNotificationReissuer.php @@ -0,0 +1,25 @@ +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/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/EcfmpProvider.php b/app/Discord/FlowMeasure/Provider/EcfmpProvider.php deleted file mode 100644 index a0a48bdf..00000000 --- a/app/Discord/FlowMeasure/Provider/EcfmpProvider.php +++ /dev/null @@ -1,15 +0,0 @@ -measure = $measure; @@ -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 26bb25c3..ca44c69a 100644 --- a/app/Discord/FlowMeasure/Provider/PendingMessageInterface.php +++ b/app/Discord/FlowMeasure/Provider/PendingMessageInterface.php @@ -3,7 +3,6 @@ namespace App\Discord\FlowMeasure\Provider; use App\Discord\FlowMeasure\Helper\NotificationReissuerInterface; -use App\Discord\Webhook\WebhookInterface; use App\Enums\DiscordNotificationType; use App\Models\FlowMeasure; @@ -18,5 +17,5 @@ public function type(): DiscordNotificationType; public function reissue(): NotificationReissuerInterface; - public function webhook(): ?WebhookInterface; + 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/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 @@ +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/FlowMeasure.php b/app/Models/FlowMeasure.php index 0bd174c4..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; @@ -133,11 +132,28 @@ public function discordNotifications(): BelongsToMany ->withTimestamps(); } + public function discordNotificationsOfType(array $types): BelongsToMany + { + 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 notifiedEcfmpNotifications(): BelongsToMany + { + return $this->discordNotificationsOfType([DiscordNotificationTypeEnum::FLOW_MEASURE_NOTIFIED]); + } + public function activatedDivisionNotifications(): BelongsToMany { return $this->divisionNotificationsOfType([DiscordNotificationTypeEnum::FLOW_MEASURE_ACTIVATED]); @@ -283,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/Providers/DiscordServiceProvider.php b/app/Providers/DiscordServiceProvider.php index baf8e772..5a45d58e 100644 --- a/app/Providers/DiscordServiceProvider.php +++ b/app/Providers/DiscordServiceProvider.php @@ -3,13 +3,17 @@ namespace App\Providers; 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\DivisionWebhookMessageProvider; -use App\Discord\FlowMeasure\Provider\MessageProvider; +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; @@ -48,7 +52,24 @@ public function register(): void ) ); + $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 diff --git a/app/Repository/FlowMeasureNotification/ActiveRepository.php b/app/Repository/FlowMeasureNotification/ActiveRepository.php index 3f76ae08..0400ae90 100644 --- a/app/Repository/FlowMeasureNotification/ActiveRepository.php +++ b/app/Repository/FlowMeasureNotification/ActiveRepository.php @@ -4,6 +4,7 @@ use App\Enums\DiscordNotificationType; use App\Models\FlowMeasure; +use Illuminate\Database\Query\Builder; use Illuminate\Support\Collection; class ActiveRepository implements RepositoryInterface @@ -12,7 +13,32 @@ public function flowMeasuresToBeSentToEcfmp(): Collection { return FlowMeasure::active() ->withoutEcfmpNotificationOfTypeForIdentifier($this->notificationType()) - ->get(); + ->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 diff --git a/app/Repository/FlowMeasureNotification/ExpiredRepository.php b/app/Repository/FlowMeasureNotification/ExpiredRepository.php index 2c152f4c..6f9da9b4 100644 --- a/app/Repository/FlowMeasureNotification/ExpiredRepository.php +++ b/app/Repository/FlowMeasureNotification/ExpiredRepository.php @@ -29,7 +29,13 @@ public function flowMeasuresToBeSentToEcfmp(): Collection DiscordNotificationType::FLOW_MEASURE_EXPIRED, DiscordNotificationType::FLOW_MEASURE_WITHDRAWN, ]) - ->get(); + ->get() + ->map( + fn (FlowMeasure $measure) => new FlowMeasureForNotification( + $measure, + false + ) + ); } public function flowMeasuresForNotification(): Collection 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 @@ +where('start_time', '>', Carbon::now()) ->withoutEcfmpNotificationOfTypeForIdentifier($this->notificationType()) ->withoutEcfmpNotificationOfType(DiscordNotificationType::FLOW_MEASURE_ACTIVATED) - ->get(); + ->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 diff --git a/app/Repository/FlowMeasureNotification/WithdrawnRepository.php b/app/Repository/FlowMeasureNotification/WithdrawnRepository.php index 0e2e28b7..aaeaad31 100644 --- a/app/Repository/FlowMeasureNotification/WithdrawnRepository.php +++ b/app/Repository/FlowMeasureNotification/WithdrawnRepository.php @@ -15,7 +15,13 @@ public function flowMeasuresToBeSentToEcfmp(): Collection return $this->baseQueryForEcfmp()->notified() ->union($this->baseQueryForEcfmp()->active()) ->orderBy('id') - ->get(); + ->get() + ->map( + fn (FlowMeasure $measure) => new FlowMeasureForNotification( + $measure, + false + ) + ); } public function flowMeasuresForNotification(): Collection diff --git a/composer.json b/composer.json index 467c9c7f..3749f7f8 100644 --- a/composer.json +++ b/composer.json @@ -8,7 +8,7 @@ ], "license": "MIT", "require": { - "php": "^8.1", + "php": "^8.2", "ext-grpc": "*", "ext-json": "*", "doctrine/dbal": "^3.3", diff --git a/config/discord.php b/config/discord.php index e2ab8fec..28027ba8 100644 --- a/config/discord.php +++ b/config/discord.php @@ -8,4 +8,6 @@ '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/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 index c66ce7a8..c0fa3be1 100644 --- a/tests/Discord/DiscordServiceMessageSenderTest.php +++ b/tests/Discord/DiscordServiceMessageSenderTest.php @@ -5,7 +5,7 @@ use App\Discord\Client\ClientFactoryInterface; use App\Discord\Exception\DiscordServiceException; use App\Discord\Message\Embed\EmbedCollection; -use App\Discord\Message\MessageInterface; +use App\Discord\Message\EcfmpMessageInterface; use Ecfmp_discord\CreateRequest; use Ecfmp_discord\CreateResponse; use Ecfmp_discord\DiscordClient; @@ -21,7 +21,7 @@ class DiscordServiceMessageSenderTest extends TestCase { private readonly ClientFactoryInterface $clientFactory; private readonly DiscordClient $client; - private readonly MessageInterface $message; + private readonly EcfmpMessageInterface $message; private readonly EmbedCollection $embeds; private readonly DiscordEmbeds $discordEmbeds; private readonly CreateResponse $response; @@ -37,13 +37,15 @@ public function setUp(): void $this->clientFactory = Mockery::mock(ClientFactoryInterface::class); $this->client = Mockery::mock(DiscordClient::class); - $this->message = Mockery::mock(MessageInterface::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); } @@ -68,7 +70,7 @@ public function testItSendsAMessageAndReturnsTheId() $this->client->shouldReceive('Create')->with(Mockery::on( fn (CreateRequest $request) => $request->getContent() === 'content' && count($request->getEmbeds()) === 1 && - $request->getEmbeds()[0] == $this->discordEmbeds + $request->getEmbeds()[0] == $this->discordEmbeds && $request->getChannel() === 'channel1' ), [ 'authorization' => [config('discord.service_token')], 'x-client-request-id' => ['client-request-id'], @@ -90,7 +92,7 @@ public function testItThrowsAnExceptionIfStatusIsNotOk() $this->client->shouldReceive('Create')->with(Mockery::on( fn (CreateRequest $request) => $request->getContent() === 'content' && count($request->getEmbeds()) === 1 && - $request->getEmbeds()[0] == $this->discordEmbeds + $request->getEmbeds()[0] == $this->discordEmbeds && $request->getChannel() === 'channel1' ), [ 'authorization' => [config('discord.service_token')], 'x-client-request-id' => ['client-request-id'], diff --git a/tests/Discord/FlowMeasure/Content/FlowMeasureRecipientsFactoryTest.php b/tests/Discord/FlowMeasure/Content/FlowMeasureRecipientsFactoryTest.php index f037de7b..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,84 +33,108 @@ 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->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->pendingMessage - ->shouldReceive('webhook') - ->andReturn($this->webhook); $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->pendingMessage - ->shouldReceive('webhook') - ->andReturn($this->webhook); $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->pendingMessage - ->shouldReceive('webhook') - ->andReturn($this->webhook); + $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(); $fir = FlightInformationRegion::factory()->create(); $divisionWebhook->flightInformationRegions()->sync([$fir->id => ['tag' => '1234']]); - $this->pendingMessage - ->shouldReceive('webhook') - ->andReturn($this->webhook); $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(); @@ -116,20 +142,17 @@ public function testItReturnsMultipleDivisionRecipients() $divisionWebhook->flightInformationRegions()->attach([$fir->id => ['tag' => '1234']]); $fir2 = FlightInformationRegion::factory()->create(); $divisionWebhook->flightInformationRegions()->attach([$fir2->id => ['tag' => '5678']]); - $this->pendingMessage - ->shouldReceive('webhook') - ->andReturn($this->webhook); $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(); @@ -137,49 +160,134 @@ public function testItIgnoresRecipientsThatAreNotForFlowMeasure() $divisionWebhook->flightInformationRegions()->attach([$fir->id => ['tag' => '1234']]); $fir2 = FlightInformationRegion::factory()->create(); $divisionWebhook->flightInformationRegions()->attach([$fir2->id => ['tag' => '5678']]); - $this->pendingMessage - ->shouldReceive('webhook') - ->andReturn($this->webhook); $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->pendingMessage - ->shouldReceive('webhook') - ->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->pendingMessage - ->shouldReceive('webhook') - ->andReturn(null); - $notification = $this->flowMeasure->divisionDiscordNotifications()->create( + $notification = $this->flowMeasure->discordNotifications()->create( [ - 'content' => '', - 'embeds' => [], + 'remote_id' => 'abc', ], [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -191,26 +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->pendingMessage - ->shouldReceive('webhook') - ->andReturn(null); - $notification = $this->flowMeasure->divisionDiscordNotifications()->create( + $notification = $this->flowMeasure->discordNotifications()->create( [ - 'content' => '', - 'embeds' => [], + 'remote_id' => 'abc', ], [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -222,27 +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->pendingMessage - ->shouldReceive('webhook') - ->andReturn(null); - $notification = $this->flowMeasure->divisionDiscordNotifications()->create( + $notification = $this->flowMeasure->discordNotifications()->create( [ - 'content' => '', - 'embeds' => [], + 'remote_id' => 'abc', ], [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -254,27 +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->pendingMessage - ->shouldReceive('webhook') - ->andReturn(null); - $notification = $this->flowMeasure->divisionDiscordNotifications()->create( + $notification = $this->flowMeasure->discordNotifications()->create( [ - 'content' => '', - 'embeds' => [], + 'remote_id' => 'abc', ], [ 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( @@ -286,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 b59f7651..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(null); $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(null); $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/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/Provider/PendingDiscordMessageTest.php b/tests/Discord/FlowMeasure/Provider/PendingDiscordWebhookMessageTest.php similarity index 73% rename from tests/Discord/FlowMeasure/Provider/PendingDiscordMessageTest.php rename to tests/Discord/FlowMeasure/Provider/PendingDiscordWebhookMessageTest.php index c8c6fdd5..06275ced 100644 --- a/tests/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..860a6d78 --- /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/Jobs/SendDiscordNotificationsTest.php b/tests/Jobs/SendDiscordNotificationsTest.php index 28fca9ed..1cf9f1d2 100644 --- a/tests/Jobs/SendDiscordNotificationsTest.php +++ b/tests/Jobs/SendDiscordNotificationsTest.php @@ -2,6 +2,7 @@ namespace Tests\Jobs; +use App\Discord\FlowMeasure\Generator\EcfmpFlowMeasureMessageGenerator; use App\Discord\Message\Sender\DivisionWebhookSender; use App\Jobs\SendDiscordNotifications; use Illuminate\Support\Facades\Config; @@ -18,6 +19,10 @@ public function testItRunsNotificationSending() $senderMock->shouldReceive('sendDiscordMessages')->once(); $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(); } @@ -30,6 +35,10 @@ public function testItDoesntRunsNotificationSendingIfSwitchedOff() $senderMock->shouldReceive('sendDiscordMessages')->never(); $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 a2083dd3..cb840b34 100644 --- a/tests/Repository/FlowMeasureNotification/ActiveRepositoryTest.php +++ b/tests/Repository/FlowMeasureNotification/ActiveRepositoryTest.php @@ -129,7 +129,81 @@ function (FlowMeasure $measure) { $this->assertEquals( [$measure1->id, $measure2->id, $measure3->id, $measure4->id], - $this->repository->flowMeasuresToBeSentToEcfmp()->pluck('id')->toArray() + $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 2e151e4b..bed3f6e0 100644 --- a/tests/Repository/FlowMeasureNotification/ExpiredRepositoryTest.php +++ b/tests/Repository/FlowMeasureNotification/ExpiredRepositoryTest.php @@ -242,7 +242,30 @@ function (FlowMeasure $measure) { $this->assertEquals( [$flowMeasure1->id, $flowMeasure2->id], - $this->repository->flowMeasuresToBeSentToEcfmp()->pluck('id')->toArray() + $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 4bdeb458..8b03500b 100644 --- a/tests/Repository/FlowMeasureNotification/NotifiedRepositoryTest.php +++ b/tests/Repository/FlowMeasureNotification/NotifiedRepositoryTest.php @@ -125,7 +125,39 @@ function (FlowMeasure $measure) { $this->assertEquals( [$measure1->id, $measure2->id, $measure3->id], - $this->repository->flowMeasuresToBeSentToEcfmp()->pluck('id')->toArray() + $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 490de257..eaaae252 100644 --- a/tests/Repository/FlowMeasureNotification/WithdrawnRepositoryTest.php +++ b/tests/Repository/FlowMeasureNotification/WithdrawnRepositoryTest.php @@ -231,7 +231,31 @@ public function testItReturnsFlowMeasuresToSendToEcfmp() $this->assertEquals( [$measure1->id, $measure2->id], - $this->repository->flowMeasuresToBeSentToEcfmp()->pluck('id')->toArray() + $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); + } } From 558bd96cc0a7a65c1ec7986d1b2d7208885b0740 Mon Sep 17 00:00:00 2001 From: Andy Ford Date: Wed, 18 Oct 2023 18:13:22 +0100 Subject: [PATCH 21/34] build: protobuf bump --- protobuf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protobuf b/protobuf index 27e7dd91..61010cae 160000 --- a/protobuf +++ b/protobuf @@ -1 +1 @@ -Subproject commit 27e7dd910bb44902d4cf90d91a6e115832cfa0a5 +Subproject commit 61010caedad758308b17f42ebe844f11b00721b1 From 14f9e0d7f9442f4d395eb9bc8972597c3089a866 Mon Sep 17 00:00:00 2001 From: Andy Ford Date: Wed, 18 Oct 2023 18:25:33 +0100 Subject: [PATCH 22/34] build: grpc in actions --- .github/workflows/test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3b8c5faf..64042c45 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -31,6 +31,7 @@ jobs: php-version: ${{ matrix.php }} coverage: pcov tools: composer:${{ matrix.composer }} + extensions: grpc # Setting up composer dependencies - name: Get Composer Cache Directory From 168c360f0a4c685c2900d61386f225d1d529852f Mon Sep 17 00:00:00 2001 From: Andy Ford Date: Wed, 18 Oct 2023 18:26:04 +0100 Subject: [PATCH 23/34] test: fix test name clash --- tests/Discord/FlowMeasure/Provider/PendingEcfmpMessageTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Discord/FlowMeasure/Provider/PendingEcfmpMessageTest.php b/tests/Discord/FlowMeasure/Provider/PendingEcfmpMessageTest.php index 860a6d78..77bc5d7d 100644 --- a/tests/Discord/FlowMeasure/Provider/PendingEcfmpMessageTest.php +++ b/tests/Discord/FlowMeasure/Provider/PendingEcfmpMessageTest.php @@ -9,7 +9,7 @@ use Mockery; use Tests\TestCase; -class PendingDiscordWebhookMessageTest extends TestCase +class PendingEcfmpMessageTest extends TestCase { private readonly FlowMeasure $measure; private readonly DiscordNotificationType $type; From bddd23a6d7d5342437ceae1e0f34f4762a62dd0e Mon Sep 17 00:00:00 2001 From: Andy Ford Date: Wed, 18 Oct 2023 18:30:55 +0100 Subject: [PATCH 24/34] test: fix tests --- .../FlowMeasure/Provider/DivisionWebhookMessageProvider.php | 2 +- tests/Discord/FlowMeasure/Message/MessageGeneratorTest.php | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/Discord/FlowMeasure/Provider/DivisionWebhookMessageProvider.php b/app/Discord/FlowMeasure/Provider/DivisionWebhookMessageProvider.php index 94b30083..0eacda14 100644 --- a/app/Discord/FlowMeasure/Provider/DivisionWebhookMessageProvider.php +++ b/app/Discord/FlowMeasure/Provider/DivisionWebhookMessageProvider.php @@ -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/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); From 43fe2614fc36ce89a51723544c2024c0b16b54ad Mon Sep 17 00:00:00 2001 From: Andy Ford Date: Wed, 18 Oct 2023 18:31:16 +0100 Subject: [PATCH 25/34] style: pint --- app/Discord/FlowMeasure/Helper/EcfmpNotificationReissuer.php | 1 - .../2023_10_14_150204_add_columns_to_flow_measures_table.php | 2 +- .../migrations/2023_10_14_150428_update_flow_measure_data.php | 3 +-- .../FlowMeasureNotification/ExpiredRepositoryTest.php | 1 - 4 files changed, 2 insertions(+), 5 deletions(-) diff --git a/app/Discord/FlowMeasure/Helper/EcfmpNotificationReissuer.php b/app/Discord/FlowMeasure/Helper/EcfmpNotificationReissuer.php index cc971e28..7482fb91 100644 --- a/app/Discord/FlowMeasure/Helper/EcfmpNotificationReissuer.php +++ b/app/Discord/FlowMeasure/Helper/EcfmpNotificationReissuer.php @@ -2,7 +2,6 @@ namespace App\Discord\FlowMeasure\Helper; -use App\Discord\FlowMeasure\Helper\NotificationReissuerInterface; use App\Enums\DiscordNotificationType; use App\Repository\FlowMeasureNotification\FlowMeasureForNotification; 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 index 5b6fb535..2103467f 100644 --- 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 @@ -14,7 +14,7 @@ public function up(): void $table->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'); 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 index 309f71a2..b9a324df 100644 --- a/database/migrations/2023_10_14_150428_update_flow_measure_data.php +++ b/database/migrations/2023_10_14_150428_update_flow_measure_data.php @@ -4,8 +4,7 @@ use App\Models\FlowMeasure; use Illuminate\Database\Migrations\Migration; -return new class extends Migration -{ +return new class () extends Migration { /** * Run the migrations. */ diff --git a/tests/Repository/FlowMeasureNotification/ExpiredRepositoryTest.php b/tests/Repository/FlowMeasureNotification/ExpiredRepositoryTest.php index bed3f6e0..4ce7ecec 100644 --- a/tests/Repository/FlowMeasureNotification/ExpiredRepositoryTest.php +++ b/tests/Repository/FlowMeasureNotification/ExpiredRepositoryTest.php @@ -8,7 +8,6 @@ use App\Models\FlowMeasure; use App\Repository\FlowMeasureNotification\ExpiredRepository; use Carbon\Carbon; -use Illuminate\Support\Facades\DB; use Illuminate\Support\Str; use Tests\TestCase; From d96ee7015b8a6cde4b90fa60ac777ae77683cfbd Mon Sep 17 00:00:00 2001 From: Andy Ford Date: Wed, 18 Oct 2023 18:37:36 +0100 Subject: [PATCH 26/34] build: checkout discord service on actions --- .github/workflows/test.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 64042c45..59895990 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -21,9 +21,15 @@ 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: Configure PHP uses: shivammathur/setup-php@v2 From 095a347d74fcdd98959ff8335990e548414257f0 Mon Sep 17 00:00:00 2001 From: Andy Ford Date: Wed, 18 Oct 2023 18:42:24 +0100 Subject: [PATCH 27/34] build: move repo --- .github/workflows/test.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 59895990..724508c8 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -29,7 +29,10 @@ jobs: uses: actions/checkout@v4 with: repository: ecfmp/discord - path: ../ecfmp-discord + path: ecfmp-discord + + - name: Move Discord Service + run: mv ecfmp-discord ../ecfmp-discord - name: Configure PHP uses: shivammathur/setup-php@v2 From d45d82c2ae81dee485faa59a4731e91c11443e32 Mon Sep 17 00:00:00 2001 From: Andy Ford Date: Wed, 18 Oct 2023 19:23:02 +0100 Subject: [PATCH 28/34] build: protobuf in actions --- .github/workflows/test.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 724508c8..d8f0eb70 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -34,6 +34,9 @@ jobs: - 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 with: From c4a5ab9010539a4f93ce66fb8537d9f06be66f9a Mon Sep 17 00:00:00 2001 From: Andy Ford Date: Wed, 18 Oct 2023 19:49:14 +0100 Subject: [PATCH 29/34] build: protobuf --- protobuf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protobuf b/protobuf index 61010cae..f7271c82 160000 --- a/protobuf +++ b/protobuf @@ -1 +1 @@ -Subproject commit 61010caedad758308b17f42ebe844f11b00721b1 +Subproject commit f7271c820b26fa558287cbcaf7f997aabf2e880a From 2673d82d2bec422db73af6bf862dd96f50566ed6 Mon Sep 17 00:00:00 2001 From: Andy Ford Date: Thu, 19 Oct 2023 21:07:07 +0100 Subject: [PATCH 30/34] fix: unique discord job Prevents cache locks --- app/Jobs/SendDiscordNotifications.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/Jobs/SendDiscordNotifications.php b/app/Jobs/SendDiscordNotifications.php index ca64d390..90c8c445 100644 --- a/app/Jobs/SendDiscordNotifications.php +++ b/app/Jobs/SendDiscordNotifications.php @@ -19,6 +19,9 @@ class SendDiscordNotifications implements ShouldQueue, ShouldBeUnique use Queueable; use SerializesModels; + // Unique for 2 minutes + public $uniqueFor = 120; + private readonly DivisionWebhookSender $sender; private readonly EcfmpFlowMeasureMessageGenerator $generator; From bd3e00ad5e019a1d0eae50c5b67fbfeb8fe7bf98 Mon Sep 17 00:00:00 2001 From: Andy Ford Date: Thu, 19 Oct 2023 21:08:03 +0100 Subject: [PATCH 31/34] fix: unique network data job --- app/Jobs/UpdateNetworkData.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/Jobs/UpdateNetworkData.php b/app/Jobs/UpdateNetworkData.php index b6ad0311..6d8f3f75 100644 --- a/app/Jobs/UpdateNetworkData.php +++ b/app/Jobs/UpdateNetworkData.php @@ -18,6 +18,9 @@ class UpdateNetworkData implements ShouldQueue, ShouldBeUnique use Queueable; use SerializesModels; + // Unique for 2 minutes + public $uniqueFor = 120 + private readonly NetworkDataDownloader $networkDataDownloader; public function __construct(NetworkDataDownloader $networkDataDownloader) From 6b97ceda5a7274edc148893f40f8dbf6ce6434a8 Mon Sep 17 00:00:00 2001 From: Andy Ford Date: Thu, 19 Oct 2023 21:21:37 +0100 Subject: [PATCH 32/34] refactor: remove ecfmp webhook code --- .../FlowMeasure/Webhook/WebhookMapper.php | 7 +- app/Discord/Webhook/EcfmpWebhook.php | 21 --- app/Providers/DiscordServiceProvider.php | 2 - .../Helper/NotificationReissuerTest.php | 103 ++--------- .../Filter/ActivatedWebhookFilterTest.php | 83 --------- .../Filter/ExpiredWebhookFilterTest.php | 163 ------------------ .../Filter/NotifiedWebhookFilterTest.php | 83 --------- .../Filter/WithdrawnWebhookFilterTest.php | 113 ------------ .../FlowMeasure/Webhook/WebhookMapperTest.php | 18 +- tests/Discord/Webhook/EcfmpWebhookTest.php | 37 ---- 10 files changed, 22 insertions(+), 608 deletions(-) delete mode 100644 app/Discord/Webhook/EcfmpWebhook.php delete mode 100644 tests/Discord/Webhook/EcfmpWebhookTest.php diff --git a/app/Discord/FlowMeasure/Webhook/WebhookMapper.php b/app/Discord/FlowMeasure/Webhook/WebhookMapper.php index d4c7a13b..4f6c4e5e 100644 --- a/app/Discord/FlowMeasure/Webhook/WebhookMapper.php +++ b/app/Discord/FlowMeasure/Webhook/WebhookMapper.php @@ -3,7 +3,6 @@ namespace App\Discord\FlowMeasure\Webhook; use App\Discord\FlowMeasure\Webhook\Filter\FilterInterface; -use App\Discord\Webhook\EcfmpWebhook; use App\Discord\Webhook\WebhookInterface; use App\Models\FlightInformationRegion; use App\Models\FlowMeasure; @@ -12,17 +11,15 @@ class WebhookMapper implements MapperInterface { private readonly FilterInterface $filter; - private readonly EcfmpWebhook $ecfmpWebhook; - public function __construct(FilterInterface $filter, EcfmpWebhook $ecfmpWebhook) + public function __construct(FilterInterface $filter) { $this->filter = $filter; - $this->ecfmpWebhook = $ecfmpWebhook; } public function mapToWebhooks(FlowMeasure $measure): Collection { - return Collection::make([$this->ecfmpWebhook]) + return Collection::make() ->merge( $measure->notifiedFlightInformationRegions->map( fn (FlightInformationRegion $fir) => $fir->divisionDiscordWebhooks diff --git a/app/Discord/Webhook/EcfmpWebhook.php b/app/Discord/Webhook/EcfmpWebhook.php deleted file mode 100644 index f706d701..00000000 --- a/app/Discord/Webhook/EcfmpWebhook.php +++ /dev/null @@ -1,21 +0,0 @@ -app->singleton(DiscordWebhookInterface::class, function () { return new DiscordWebhookSender(); }); - $this->app->singleton(EcfmpWebhook::class); $this->app->singleton( DivisionWebhookSender::class, fn () => new DivisionWebhookSender( diff --git a/tests/Discord/FlowMeasure/Helper/NotificationReissuerTest.php b/tests/Discord/FlowMeasure/Helper/NotificationReissuerTest.php index 143ef2a4..74e943c8 100644 --- a/tests/Discord/FlowMeasure/Helper/NotificationReissuerTest.php +++ b/tests/Discord/FlowMeasure/Helper/NotificationReissuerTest.php @@ -3,7 +3,6 @@ namespace Tests\Discord\FlowMeasure\Helper; use App\Discord\FlowMeasure\Helper\NotificationReissuer; -use App\Discord\Webhook\EcfmpWebhook; use App\Enums\DiscordNotificationType as DiscordNotificationTypeEnum; use App\Models\DivisionDiscordNotification; use App\Models\DiscordNotificationType; @@ -14,11 +13,14 @@ class NotificationReissuerTest extends TestCase { private readonly FlowMeasure $flowMeasure; + private readonly DivisionDiscordWebhook $webhook; public function setUp(): void { parent::setUp(); $this->flowMeasure = FlowMeasure::factory()->create(); + $this->webhook = DivisionDiscordWebhook::factory() + ->create(); } public function testItHasAType() @@ -29,7 +31,7 @@ public function testItHasAType() new NotificationReissuer( $this->flowMeasure, DiscordNotificationTypeEnum::FLOW_MEASURE_NOTIFIED, - new EcfmpWebhook() + $this->webhook ) )->type() ); @@ -43,7 +45,7 @@ public function testItHasAFlowMeasure() new NotificationReissuer( $this->flowMeasure, DiscordNotificationTypeEnum::FLOW_MEASURE_NOTIFIED, - new EcfmpWebhook() + $this->webhook ) )->measure() ); @@ -68,7 +70,7 @@ public function testItsAReissueIfItsNotifiedAndTheIdentifierHasChanged() new NotificationReissuer( $this->flowMeasure, DiscordNotificationTypeEnum::FLOW_MEASURE_NOTIFIED, - new EcfmpWebhook() + $this->webhook ) )->isReissuedNotification() ); @@ -93,7 +95,7 @@ public function testItsAReissueIfItsActivatedAndTheIdentifierHasChanged() new NotificationReissuer( $this->flowMeasure, DiscordNotificationTypeEnum::FLOW_MEASURE_ACTIVATED, - new EcfmpWebhook() + $this->webhook ) )->isReissuedNotification() ); @@ -118,45 +120,7 @@ public function testItsAReissueIfItWasNotifiedAndTheIdentifierHasChangedForActiv new NotificationReissuer( $this->flowMeasure, DiscordNotificationTypeEnum::FLOW_MEASURE_ACTIVATED, - new EcfmpWebhook() - ) - )->isReissuedNotification() - ); - } - - public function testItsAReissueIfItsNotifiedOnAEcfmpWebhook() - { - $previousNotificationEcfmp = DivisionDiscordNotification::factory() - ->create(); - - $divisionWebhook = DivisionDiscordWebhook::factory()->create(); - $previousDivisionNotification = DivisionDiscordNotification::factory() - ->toDivisionWebhook($divisionWebhook) - ->create(); - - $this->flowMeasure->divisionDiscordNotifications()->sync( - [ - $previousNotificationEcfmp->id => [ - 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( - DiscordNotificationTypeEnum::FLOW_MEASURE_NOTIFIED - ), - 'notified_as' => 'notthis', - ], - $previousDivisionNotification->id => [ - 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( - DiscordNotificationTypeEnum::FLOW_MEASURE_NOTIFIED - ), - 'notified_as' => $this->flowMeasure->identifier, - ], - ] - ); - - $this->assertTrue( - ( - new NotificationReissuer( - $this->flowMeasure, - DiscordNotificationTypeEnum::FLOW_MEASURE_NOTIFIED, - new EcfmpWebhook() + $this->webhook ) )->isReissuedNotification() ); @@ -169,9 +133,8 @@ public function testItsAReissueIfItsNotifiedOnADifferentDivisionWebhook() ->toDivisionWebhook($otherDivisionWebhook) ->create(); - $divisionWebhook = DivisionDiscordWebhook::factory()->create(); $previousDivisionNotification = DivisionDiscordNotification::factory() - ->toDivisionWebhook($divisionWebhook) + ->toDivisionWebhook($this->webhook) ->create(); $this->flowMeasure->divisionDiscordNotifications()->sync( @@ -196,7 +159,7 @@ public function testItsAReissueIfItsNotifiedOnADifferentDivisionWebhook() new NotificationReissuer( $this->flowMeasure, DiscordNotificationTypeEnum::FLOW_MEASURE_NOTIFIED, - $divisionWebhook + $this->webhook ) )->isReissuedNotification() ); @@ -209,9 +172,8 @@ public function testItsAReissueIfItsActivatedOnADifferentDivisionWebhook() ->toDivisionWebhook($otherDivisionWebhook) ->create(); - $divisionWebhook = DivisionDiscordWebhook::factory()->create(); $previousDivisionNotification = DivisionDiscordNotification::factory() - ->toDivisionWebhook($divisionWebhook) + ->toDivisionWebhook($this->webhook) ->create(); $this->flowMeasure->divisionDiscordNotifications()->sync( @@ -236,34 +198,7 @@ public function testItsAReissueIfItsActivatedOnADifferentDivisionWebhook() new NotificationReissuer( $this->flowMeasure, DiscordNotificationTypeEnum::FLOW_MEASURE_ACTIVATED, - $divisionWebhook - ) - )->isReissuedNotification() - ); - } - - public function testItsAReissueIfItsActivatedOnAEcfmpWebhook() - { - $previousNotification = DivisionDiscordNotification::factory() - ->create(); - - $this->flowMeasure->divisionDiscordNotifications()->sync( - [ - $previousNotification->id => [ - 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( - DiscordNotificationTypeEnum::FLOW_MEASURE_NOTIFIED - ), - 'notified_as' => 'nothis', - ], - ] - ); - - $this->assertTrue( - ( - new NotificationReissuer( - $this->flowMeasure, - DiscordNotificationTypeEnum::FLOW_MEASURE_NOTIFIED, - new EcfmpWebhook() + $this->webhook ) )->isReissuedNotification() ); @@ -288,7 +223,7 @@ public function testItIsNotAReissueIfItsNotifiedAndTheIdentifierHasNotChanged() new NotificationReissuer( $this->flowMeasure, DiscordNotificationTypeEnum::FLOW_MEASURE_NOTIFIED, - new EcfmpWebhook() + $this->webhook ) )->isReissuedNotification() ); @@ -313,7 +248,7 @@ public function testItIsNotAReissueIfItsActivatedAndTheIdentifierHasNotChanged() new NotificationReissuer( $this->flowMeasure, DiscordNotificationTypeEnum::FLOW_MEASURE_ACTIVATED, - new EcfmpWebhook() + $this->webhook ) )->isReissuedNotification() ); @@ -338,7 +273,7 @@ public function testItIsNotAReissueIfItsNotifiedAndThenActivatedTheIdentifierHas new NotificationReissuer( $this->flowMeasure, DiscordNotificationTypeEnum::FLOW_MEASURE_ACTIVATED, - new EcfmpWebhook() + $this->webhook ) )->isReissuedNotification() ); @@ -351,7 +286,7 @@ public function testItIsNotAReissueIfItsNotifiedAndNeverBeenNotified() new NotificationReissuer( $this->flowMeasure, DiscordNotificationTypeEnum::FLOW_MEASURE_NOTIFIED, - new EcfmpWebhook() + $this->webhook ) )->isReissuedNotification() ); @@ -364,7 +299,7 @@ public function testItIsNotAReissueIfItsNotifiedAndNeverBeenActivated() new NotificationReissuer( $this->flowMeasure, DiscordNotificationTypeEnum::FLOW_MEASURE_ACTIVATED, - new EcfmpWebhook() + $this->webhook ) )->isReissuedNotification() ); @@ -389,7 +324,7 @@ public function testItIsNotAReissueIfItsWithdrawn() new NotificationReissuer( $this->flowMeasure, DiscordNotificationTypeEnum::FLOW_MEASURE_WITHDRAWN, - new EcfmpWebhook() + $this->webhook ) )->isReissuedNotification() ); @@ -414,7 +349,7 @@ public function testItIsNotAReissueIfItsExpired() new NotificationReissuer( $this->flowMeasure, DiscordNotificationTypeEnum::FLOW_MEASURE_EXPIRED, - new EcfmpWebhook() + $this->webhook ) )->isReissuedNotification() ); diff --git a/tests/Discord/FlowMeasure/Webhook/Filter/ActivatedWebhookFilterTest.php b/tests/Discord/FlowMeasure/Webhook/Filter/ActivatedWebhookFilterTest.php index 94f9877f..d760f3fb 100644 --- a/tests/Discord/FlowMeasure/Webhook/Filter/ActivatedWebhookFilterTest.php +++ b/tests/Discord/FlowMeasure/Webhook/Filter/ActivatedWebhookFilterTest.php @@ -3,7 +3,6 @@ namespace Tests\Discord\FlowMeasure\Webhook\Filter; use App\Discord\FlowMeasure\Webhook\Filter\ActivatedWebhookFilter; -use App\Discord\Webhook\EcfmpWebhook; use App\Enums\DiscordNotificationType as DiscordNotificationTypeEnum; use App\Models\DivisionDiscordNotification; use App\Models\DiscordNotificationType; @@ -14,97 +13,15 @@ class ActivatedWebhookFilterTest extends TestCase { private readonly ActivatedWebhookFilter $filter; - private readonly EcfmpWebhook $ecfmpWebhook; private readonly DivisionDiscordWebhook $divisionDiscordWebhook; public function setUp(): void { parent::setUp(); $this->filter = $this->app->make(ActivatedWebhookFilter::class); - $this->ecfmpWebhook = $this->app->make(EcfmpWebhook::class); $this->divisionDiscordWebhook = DivisionDiscordWebhook::factory()->create(); } - public function testItShouldUseWebhookIfFlowMeasureHasNeverBeenActivatedToEcfmpWebhook() - { - $measure = FlowMeasure::factory()->create(); - $this->assertTrue( - $this->filter->shouldUseWebhook( - $measure, - $this->ecfmpWebhook - ) - ); - } - - public function testItShouldUseWebhookIfFlowMeasureHasOnlyBeenNotifiedToEcfmpWebhook() - { - $measure = FlowMeasure::factory()->create(); - $discordNotification = DivisionDiscordNotification::factory()->create(); - $measure->divisionDiscordNotifications()->attach( - [ - $discordNotification->id => [ - 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( - DiscordNotificationTypeEnum::FLOW_MEASURE_NOTIFIED - ), - 'notified_as' => $measure->identifier, - ], - ] - ); - - $this->assertTrue( - $this->filter->shouldUseWebhook( - $measure, - $this->ecfmpWebhook - ) - ); - } - - public function testItShouldUseWebhookIfFlowMeasureHasBeenActivatedAsDifferentIdentifierToEcfmpWebhook() - { - $measure = FlowMeasure::factory()->create(); - $discordNotification = DivisionDiscordNotification::factory()->create(); - $measure->divisionDiscordNotifications()->attach( - [ - $discordNotification->id => [ - 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( - DiscordNotificationTypeEnum::FLOW_MEASURE_ACTIVATED - ), - 'notified_as' => 'NOTHIS', - ], - ] - ); - - $this->assertTrue( - $this->filter->shouldUseWebhook( - $measure, - $this->ecfmpWebhook - ) - ); - } - - public function testItShouldNotUseWebhookIfFlowMeasureHasBeenActivatedToEcfmpWebhook() - { - $measure = FlowMeasure::factory()->create(); - $discordNotification = DivisionDiscordNotification::factory()->create(); - $measure->divisionDiscordNotifications()->attach( - [ - $discordNotification->id => [ - 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( - DiscordNotificationTypeEnum::FLOW_MEASURE_ACTIVATED - ), - 'notified_as' => $measure->identifier, - ], - ] - ); - - $this->assertFalse( - $this->filter->shouldUseWebhook( - $measure, - $this->ecfmpWebhook - ) - ); - } - public function testItShouldUseWebhookIfFlowMeasureHasNeverBeenActivatedToDivisionWebhook() { $measure = FlowMeasure::factory()->create(); diff --git a/tests/Discord/FlowMeasure/Webhook/Filter/ExpiredWebhookFilterTest.php b/tests/Discord/FlowMeasure/Webhook/Filter/ExpiredWebhookFilterTest.php index 13ce3250..88500b96 100644 --- a/tests/Discord/FlowMeasure/Webhook/Filter/ExpiredWebhookFilterTest.php +++ b/tests/Discord/FlowMeasure/Webhook/Filter/ExpiredWebhookFilterTest.php @@ -3,188 +3,25 @@ namespace Tests\Discord\FlowMeasure\Webhook\Filter; use App\Discord\FlowMeasure\Webhook\Filter\ExpiredWebhookFilter; -use App\Discord\Webhook\EcfmpWebhook; use App\Enums\DiscordNotificationType as DiscordNotificationTypeEnum; use App\Models\DivisionDiscordNotification; use App\Models\DiscordNotificationType; use App\Models\DivisionDiscordWebhook; use App\Models\FlowMeasure; -use Carbon\Carbon; use Tests\TestCase; class ExpiredWebhookFilterTest extends TestCase { private readonly ExpiredWebhookFilter $filter; - private readonly EcfmpWebhook $ecfmpWebhook; private readonly DivisionDiscordWebhook $divisionDiscordWebhook; public function setUp(): void { parent::setUp(); $this->filter = $this->app->make(ExpiredWebhookFilter::class); - $this->ecfmpWebhook = $this->app->make(EcfmpWebhook::class); $this->divisionDiscordWebhook = DivisionDiscordWebhook::factory()->create(); } - public function testItShouldUseEcfmpWebhookIfLotsOfNotificationsHaveBeenSentRecently() - { - $measure = FlowMeasure::factory()->create(); - DivisionDiscordNotification::factory()->count(6)->create(['division_discord_webhook_id' => null]); - - $this->assertTrue( - $this->filter->shouldUseWebhook( - $measure, - $this->ecfmpWebhook - ) - ); - } - - public function testItShouldUseEcfmpWebhookIfTheFlowMeasureHasBeenModifiedTwice() - { - $measure = FlowMeasure::factory()->create(['identifier' => 'EGTT23A-3']); - - $this->assertTrue( - $this->filter->shouldUseWebhook( - $measure, - $this->ecfmpWebhook - ) - ); - } - - public function testItShouldUseEcfmpWebhookIfThereAreOtherMeasuresActiveAroundTheTime() - { - FlowMeasure::factory() - ->withTimes(Carbon::now()->subMinutes(30), Carbon::now()->addMinutes(45)) - ->count(3) - ->create(); - - $measure = FlowMeasure::factory()->create(); - - $this->assertTrue( - $this->filter->shouldUseWebhook( - $measure, - $this->ecfmpWebhook - ) - ); - } - - public function testItShouldUseWebhookIfFlowMeasureHasOnlyBeenNotifiedToEcfmpWebhook() - { - $measure = FlowMeasure::factory()->create(['identifier' => 'EGTT23A-3']); - $discordNotification = DivisionDiscordNotification::factory()->create(); - $measure->divisionDiscordNotifications()->attach( - [ - $discordNotification->id => [ - 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( - DiscordNotificationTypeEnum::FLOW_MEASURE_NOTIFIED - ), - 'notified_as' => $measure->identifier, - ], - ] - ); - - $this->assertTrue( - $this->filter->shouldUseWebhook( - $measure, - $this->ecfmpWebhook - ) - ); - } - - public function testItShouldUseWebhookIfFlowMeasureHasOnlyBeenActivatedToEcfmpWebhook() - { - $measure = FlowMeasure::factory()->create(['identifier' => 'EGTT23A-3']); - $discordNotification = DivisionDiscordNotification::factory()->create(); - $measure->divisionDiscordNotifications()->attach( - [ - $discordNotification->id => [ - 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( - DiscordNotificationTypeEnum::FLOW_MEASURE_NOTIFIED - ), - 'notified_as' => $measure->identifier, - ], - ] - ); - - $this->assertTrue( - $this->filter->shouldUseWebhook( - $measure, - $this->ecfmpWebhook - ) - ); - } - - public function testItShouldNotUseWebhookIfFlowMeasureHasBeenWithdrawnToEcfmpWebhook() - { - $measure = FlowMeasure::factory()->create(['identifier' => 'EGTT23A-3']); - $discordNotification = DivisionDiscordNotification::factory()->create(); - $measure->divisionDiscordNotifications()->attach( - [ - $discordNotification->id => [ - 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( - DiscordNotificationTypeEnum::FLOW_MEASURE_WITHDRAWN - ), - 'notified_as' => $measure->identifier, - ], - ] - ); - - $this->assertFalse( - $this->filter->shouldUseWebhook( - $measure, - $this->ecfmpWebhook - ) - ); - } - - public function testItShouldNotUseWebhookIfFlowMeasureHasBeenExpiredToEcfmpWebhook() - { - $measure = FlowMeasure::factory()->create(['identifier' => 'EGTT23A-3']); - $discordNotification = DivisionDiscordNotification::factory()->create(); - $measure->divisionDiscordNotifications()->attach( - [ - $discordNotification->id => [ - 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( - DiscordNotificationTypeEnum::FLOW_MEASURE_EXPIRED - ), - 'notified_as' => $measure->identifier, - ], - ] - ); - - $this->assertFalse( - $this->filter->shouldUseWebhook( - $measure, - $this->ecfmpWebhook - ) - ); - } - - public function testItShouldNotUseWebhookIfDoesntMeetConditionsForEcfmpWebhook() - { - // Once revised - $measure = FlowMeasure::factory()->create(['identifier' => 'EGTT23A-2']); - - // Two other measures - FlowMeasure::factory() - ->withTimes($measure->start_time->clone()->subMinutes(2), $measure->start_time->clone()->addMinutes(2)) - ->count(2) - ->create(); - - // Not too many recently sent - DivisionDiscordNotification::factory()->count(5)->create(['division_discord_webhook_id' => null]); - DivisionDiscordNotification::factory()->count(5) - ->toDivisionWebhook(DivisionDiscordWebhook::factory()->create()) - ->create(); - - $this->assertFalse( - $this->filter->shouldUseWebhook( - $measure, - $this->ecfmpWebhook - ) - ); - } - public function testItShouldNotUseWebhookIfFlowMeasureHasOnlyBeenNotifiedToDivisionWebhook() { $measure = FlowMeasure::factory()->create(); diff --git a/tests/Discord/FlowMeasure/Webhook/Filter/NotifiedWebhookFilterTest.php b/tests/Discord/FlowMeasure/Webhook/Filter/NotifiedWebhookFilterTest.php index fbace8a0..5ab267b1 100644 --- a/tests/Discord/FlowMeasure/Webhook/Filter/NotifiedWebhookFilterTest.php +++ b/tests/Discord/FlowMeasure/Webhook/Filter/NotifiedWebhookFilterTest.php @@ -3,7 +3,6 @@ namespace Tests\Discord\FlowMeasure\Webhook\Filter; use App\Discord\FlowMeasure\Webhook\Filter\NotifiedWebhookFilter; -use App\Discord\Webhook\EcfmpWebhook; use App\Enums\DiscordNotificationType as DiscordNotificationTypeEnum; use App\Models\DivisionDiscordNotification; use App\Models\DiscordNotificationType; @@ -14,97 +13,15 @@ class NotifiedWebhookFilterTest extends TestCase { private readonly NotifiedWebhookFilter $filter; - private readonly EcfmpWebhook $ecfmpWebhook; private readonly DivisionDiscordWebhook $divisionDiscordWebhook; public function setUp(): void { parent::setUp(); $this->filter = $this->app->make(NotifiedWebhookFilter::class); - $this->ecfmpWebhook = $this->app->make(EcfmpWebhook::class); $this->divisionDiscordWebhook = DivisionDiscordWebhook::factory()->create(); } - public function testItShouldUseWebhookIfFlowMeasureHasNeverBeenActivatedOrNotifiedToEcfmpWebhook() - { - $measure = FlowMeasure::factory()->create(); - $this->assertTrue( - $this->filter->shouldUseWebhook( - $measure, - $this->ecfmpWebhook - ) - ); - } - - public function testItShouldUseWebhookIfFlowMeasureHasBeenNotifiedToEcfmpWebhookUnderDifferentIdentifier() - { - $measure = FlowMeasure::factory()->create(); - $discordNotification = DivisionDiscordNotification::factory()->create(); - $measure->divisionDiscordNotifications()->attach( - [ - $discordNotification->id => [ - 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( - DiscordNotificationTypeEnum::FLOW_MEASURE_NOTIFIED - ), - 'notified_as' => 'notme', - ], - ] - ); - - $this->assertTrue( - $this->filter->shouldUseWebhook( - $measure, - $this->ecfmpWebhook - ) - ); - } - - public function testItShouldNotUseWebhookIfFlowMeasureHasBeenNotifiedToEcfmpWebhook() - { - $measure = FlowMeasure::factory()->create(); - $discordNotification = DivisionDiscordNotification::factory()->create(); - $measure->divisionDiscordNotifications()->attach( - [ - $discordNotification->id => [ - 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( - DiscordNotificationTypeEnum::FLOW_MEASURE_NOTIFIED - ), - 'notified_as' => $measure->identifier, - ], - ] - ); - - $this->assertFalse( - $this->filter->shouldUseWebhook( - $measure, - $this->ecfmpWebhook - ) - ); - } - - public function testItShouldNotUseWebhookIfFlowMeasureHasBeenActivatedToEcfmpWebhook() - { - $measure = FlowMeasure::factory()->create(); - $discordNotification = DivisionDiscordNotification::factory()->create(); - $measure->divisionDiscordNotifications()->attach( - [ - $discordNotification->id => [ - 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( - DiscordNotificationTypeEnum::FLOW_MEASURE_ACTIVATED - ), - 'notified_as' => $measure->identifier, - ], - ] - ); - - $this->assertFalse( - $this->filter->shouldUseWebhook( - $measure, - $this->ecfmpWebhook - ) - ); - } - public function testItShouldUseWebhookIfFlowMeasureHasNeverBeenActivatedOrNotifiedToDivisionWebhook() { $measure = FlowMeasure::factory()->create(); diff --git a/tests/Discord/FlowMeasure/Webhook/Filter/WithdrawnWebhookFilterTest.php b/tests/Discord/FlowMeasure/Webhook/Filter/WithdrawnWebhookFilterTest.php index bb8d5e4c..b0a3a1c1 100644 --- a/tests/Discord/FlowMeasure/Webhook/Filter/WithdrawnWebhookFilterTest.php +++ b/tests/Discord/FlowMeasure/Webhook/Filter/WithdrawnWebhookFilterTest.php @@ -3,7 +3,6 @@ namespace Tests\Discord\FlowMeasure\Webhook\Filter; use App\Discord\FlowMeasure\Webhook\Filter\WithdrawnWebhookFilter; -use App\Discord\Webhook\EcfmpWebhook; use App\Enums\DiscordNotificationType as DiscordNotificationTypeEnum; use App\Models\DivisionDiscordNotification; use App\Models\DiscordNotificationType; @@ -14,127 +13,15 @@ class WithdrawnWebhookFilterTest extends TestCase { private readonly WithdrawnWebhookFilter $filter; - private readonly EcfmpWebhook $ecfmpWebhook; private readonly DivisionDiscordWebhook $divisionDiscordWebhook; public function setUp(): void { parent::setUp(); $this->filter = $this->app->make(WithdrawnWebhookFilter::class); - $this->ecfmpWebhook = $this->app->make(EcfmpWebhook::class); $this->divisionDiscordWebhook = DivisionDiscordWebhook::factory()->create(); } - public function testItShouldUseEcfmpWebhookIfItHasBeenNotified() - { - $measure = FlowMeasure::factory()->create(); - $discordNotification = DivisionDiscordNotification::factory()->create(); - $measure->divisionDiscordNotifications()->attach( - [ - $discordNotification->id => [ - 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( - DiscordNotificationTypeEnum::FLOW_MEASURE_NOTIFIED - ), - 'notified_as' => $measure->identifier, - ], - ] - ); - $this->assertTrue( - $this->filter->shouldUseWebhook( - $measure, - $this->ecfmpWebhook - ) - ); - } - - public function testItShouldUseEcfmpWebhookIfItHasBeenActivated() - { - $measure = FlowMeasure::factory()->create(); - $discordNotification = DivisionDiscordNotification::factory()->create(); - $measure->divisionDiscordNotifications()->attach( - [ - $discordNotification->id => [ - 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( - DiscordNotificationTypeEnum::FLOW_MEASURE_ACTIVATED - ), - 'notified_as' => $measure->identifier, - ], - ] - ); - $this->assertTrue( - $this->filter->shouldUseWebhook( - $measure, - $this->ecfmpWebhook - ) - ); - } - - public function testItShouldNotUseEcfmpWebhookIfItHasBeenWithdrawn() - { - $measure = FlowMeasure::factory()->create(); - $discordNotification = DivisionDiscordNotification::factory()->create(); - $measure->divisionDiscordNotifications()->attach( - [ - $discordNotification->id => [ - 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( - DiscordNotificationTypeEnum::FLOW_MEASURE_NOTIFIED - ), - 'notified_as' => $measure->identifier, - ], - ] - ); - $measure->divisionDiscordNotifications()->attach( - [ - $discordNotification->id => [ - 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( - DiscordNotificationTypeEnum::FLOW_MEASURE_WITHDRAWN - ), - 'notified_as' => $measure->identifier, - ], - ] - ); - - $this->assertFalse( - $this->filter->shouldUseWebhook( - $measure, - $this->ecfmpWebhook - ) - ); - } - - public function testItShouldNotUseEcfmpWebhookIfItHasBeenExpired() - { - $measure = FlowMeasure::factory()->create(); - $discordNotification = DivisionDiscordNotification::factory()->create(); - $measure->divisionDiscordNotifications()->attach( - [ - $discordNotification->id => [ - 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( - DiscordNotificationTypeEnum::FLOW_MEASURE_NOTIFIED - ), - 'notified_as' => $measure->identifier, - ], - ] - ); - $measure->divisionDiscordNotifications()->attach( - [ - $discordNotification->id => [ - 'discord_notification_type_id' => DiscordNotificationType::idFromEnum( - DiscordNotificationTypeEnum::FLOW_MEASURE_EXPIRED - ), - 'notified_as' => $measure->identifier, - ], - ] - ); - - $this->assertFalse( - $this->filter->shouldUseWebhook( - $measure, - $this->ecfmpWebhook - ) - ); - } - public function testItShouldUseDivisionWebhookIfItHasBeenNotified() { $measure = FlowMeasure::factory()->create(); diff --git a/tests/Discord/FlowMeasure/Webhook/WebhookMapperTest.php b/tests/Discord/FlowMeasure/Webhook/WebhookMapperTest.php index 8277c06e..51ddf26c 100644 --- a/tests/Discord/FlowMeasure/Webhook/WebhookMapperTest.php +++ b/tests/Discord/FlowMeasure/Webhook/WebhookMapperTest.php @@ -4,7 +4,6 @@ use App\Discord\FlowMeasure\Webhook\Filter\FilterInterface; use App\Discord\FlowMeasure\Webhook\WebhookMapper; -use App\Discord\Webhook\EcfmpWebhook; use App\Discord\Webhook\WebhookInterface; use App\Models\DivisionDiscordWebhook; use App\Models\FlightInformationRegion; @@ -17,13 +16,11 @@ class WebhookMapperTest extends TestCase { private readonly FilterInterface $filter; private readonly WebhookMapper $mapper; - private readonly EcfmpWebhook $ecfmpWebhook; public function setUp(): void { parent::setUp(); $this->filter = Mockery::mock(FilterInterface::class); - $this->ecfmpWebhook = $this->app->make(EcfmpWebhook::class); $this->mapper = $this->app->make( WebhookMapper::class, [ @@ -36,24 +33,11 @@ public function testItReturnsEmptyCollectionWhenAllWebhooksAreFiltered() { $flowMeasure = FlowMeasure::factory()->create(); $this->filter->shouldReceive('shouldUseWebhook') - ->with($flowMeasure, $this->ecfmpWebhook) - ->once() - ->andReturnFalse(); + ->never(); $this->assertEmpty($this->mapper->mapToWebhooks($flowMeasure)); } - public function testItReturnsJustEcfmpWebhookIfNoDivisionWebhooks() - { - $flowMeasure = FlowMeasure::factory()->create(); - $this->filter->shouldReceive('shouldUseWebhook') - ->with($flowMeasure, $this->ecfmpWebhook) - ->once() - ->andReturnTrue(); - - $this->assertEquals(new Collection([$this->ecfmpWebhook]), $this->mapper->mapToWebhooks($flowMeasure)); - } - public function testItFiltersDivisionWebhooks() { $flowMeasure = FlowMeasure::factory()->create(); diff --git a/tests/Discord/Webhook/EcfmpWebhookTest.php b/tests/Discord/Webhook/EcfmpWebhookTest.php deleted file mode 100644 index efd940cc..00000000 --- a/tests/Discord/Webhook/EcfmpWebhookTest.php +++ /dev/null @@ -1,37 +0,0 @@ -assertNull((new EcfmpWebhook())->id()); - } - - public function testItHasAUrl() - { - $this->assertEquals( - 'foo', - (new EcfmpWebhook())->url() - ); - } - - public function testItHasADescription() - { - $this->assertEquals( - 'ECFMP', - (new EcfmpWebhook())->description() - ); - } -} From a6bc3aa63843383a551997a49012042e4324afaf Mon Sep 17 00:00:00 2001 From: Andy Ford Date: Thu, 19 Oct 2023 21:22:35 +0100 Subject: [PATCH 33/34] fix: syntax error --- app/Jobs/UpdateNetworkData.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Jobs/UpdateNetworkData.php b/app/Jobs/UpdateNetworkData.php index 6d8f3f75..3a346d06 100644 --- a/app/Jobs/UpdateNetworkData.php +++ b/app/Jobs/UpdateNetworkData.php @@ -19,7 +19,7 @@ class UpdateNetworkData implements ShouldQueue, ShouldBeUnique use SerializesModels; // Unique for 2 minutes - public $uniqueFor = 120 + public $uniqueFor = 120; private readonly NetworkDataDownloader $networkDataDownloader; From 61cc1c94dbc17cccc42b7c9ca435c13f516b057a Mon Sep 17 00:00:00 2001 From: Andy Ford Date: Fri, 20 Oct 2023 15:03:10 +0100 Subject: [PATCH 34/34] test: fix tests --- .../Helper/NotificationReissuerTest.php | 32 ++++++++++++++----- .../FlowMeasure/Webhook/WebhookMapperTest.php | 10 ++++-- 2 files changed, 31 insertions(+), 11 deletions(-) diff --git a/tests/Discord/FlowMeasure/Helper/NotificationReissuerTest.php b/tests/Discord/FlowMeasure/Helper/NotificationReissuerTest.php index 74e943c8..c5df658b 100644 --- a/tests/Discord/FlowMeasure/Helper/NotificationReissuerTest.php +++ b/tests/Discord/FlowMeasure/Helper/NotificationReissuerTest.php @@ -53,7 +53,9 @@ public function testItHasAFlowMeasure() public function testItsAReissueIfItsNotifiedAndTheIdentifierHasChanged() { - $previousNotification = DivisionDiscordNotification::factory()->create(); + $previousNotification = DivisionDiscordNotification::factory() + ->toDivisionWebhook($this->webhook) + ->create(); $this->flowMeasure->divisionDiscordNotifications()->sync( [ $previousNotification->id => [ @@ -78,7 +80,9 @@ public function testItsAReissueIfItsNotifiedAndTheIdentifierHasChanged() public function testItsAReissueIfItsActivatedAndTheIdentifierHasChanged() { - $previousNotification = DivisionDiscordNotification::factory()->create(); + $previousNotification = DivisionDiscordNotification::factory() + ->toDivisionWebhook($this->webhook) + ->create(); $this->flowMeasure->divisionDiscordNotifications()->sync( [ $previousNotification->id => [ @@ -103,7 +107,9 @@ public function testItsAReissueIfItsActivatedAndTheIdentifierHasChanged() public function testItsAReissueIfItWasNotifiedAndTheIdentifierHasChangedForActivation() { - $previousNotification = DivisionDiscordNotification::factory()->create(); + $previousNotification = DivisionDiscordNotification::factory() + ->toDivisionWebhook($this->webhook) + ->create(); $this->flowMeasure->divisionDiscordNotifications()->sync( [ $previousNotification->id => [ @@ -206,7 +212,9 @@ public function testItsAReissueIfItsActivatedOnADifferentDivisionWebhook() public function testItIsNotAReissueIfItsNotifiedAndTheIdentifierHasNotChanged() { - $previousNotification = DivisionDiscordNotification::factory()->create(); + $previousNotification = DivisionDiscordNotification::factory() + ->toDivisionWebhook($this->webhook) + ->create(); $this->flowMeasure->divisionDiscordNotifications()->sync( [ $previousNotification->id => [ @@ -231,7 +239,9 @@ public function testItIsNotAReissueIfItsNotifiedAndTheIdentifierHasNotChanged() public function testItIsNotAReissueIfItsActivatedAndTheIdentifierHasNotChanged() { - $previousNotification = DivisionDiscordNotification::factory()->create(); + $previousNotification = DivisionDiscordNotification::factory() + ->toDivisionWebhook($this->webhook) + ->create(); $this->flowMeasure->divisionDiscordNotifications()->sync( [ $previousNotification->id => [ @@ -256,7 +266,9 @@ public function testItIsNotAReissueIfItsActivatedAndTheIdentifierHasNotChanged() public function testItIsNotAReissueIfItsNotifiedAndThenActivatedTheIdentifierHasNotChanged() { - $previousNotification = DivisionDiscordNotification::factory()->create(); + $previousNotification = DivisionDiscordNotification::factory() + ->toDivisionWebhook($this->webhook) + ->create(); $this->flowMeasure->divisionDiscordNotifications()->sync( [ $previousNotification->id => [ @@ -307,7 +319,9 @@ public function testItIsNotAReissueIfItsNotifiedAndNeverBeenActivated() public function testItIsNotAReissueIfItsWithdrawn() { - $previousNotification = DivisionDiscordNotification::factory()->create(); + $previousNotification = DivisionDiscordNotification::factory() + ->toDivisionWebhook($this->webhook) + ->create(); $this->flowMeasure->divisionDiscordNotifications()->sync( [ $previousNotification->id => [ @@ -332,7 +346,9 @@ public function testItIsNotAReissueIfItsWithdrawn() public function testItIsNotAReissueIfItsExpired() { - $previousNotification = DivisionDiscordNotification::factory()->create(); + $previousNotification = DivisionDiscordNotification::factory() + ->toDivisionWebhook($this->webhook) + ->create(); $this->flowMeasure->divisionDiscordNotifications()->sync( [ $previousNotification->id => [ diff --git a/tests/Discord/FlowMeasure/Webhook/WebhookMapperTest.php b/tests/Discord/FlowMeasure/Webhook/WebhookMapperTest.php index 51ddf26c..f27a945d 100644 --- a/tests/Discord/FlowMeasure/Webhook/WebhookMapperTest.php +++ b/tests/Discord/FlowMeasure/Webhook/WebhookMapperTest.php @@ -89,7 +89,11 @@ function (FlightInformationRegion $flightInformationRegion) { $this->filter->shouldReceive('shouldUseWebhook') ->andReturnTrue(); - $this->assertCount(3, $this->mapper->mapToWebhooks($flowMeasure)); + $this->assertCount(2, $this->mapper->mapToWebhooks($flowMeasure)); + $this->assertEquals( + DivisionDiscordWebhook::all()->pluck('id'), + $this->mapper->mapToWebhooks($flowMeasure)->map(fn (WebhookInterface $webhook) => $webhook->id()) + ); } public function testItDeduplicatesWebhooks() @@ -129,9 +133,9 @@ function (FlightInformationRegion $flightInformationRegion) use ($webhook1) { $this->filter->shouldReceive('shouldUseWebhook') ->andReturnTrue(); - $this->assertCount(3, $this->mapper->mapToWebhooks($flowMeasure)); + $this->assertCount(2, $this->mapper->mapToWebhooks($flowMeasure)); $this->assertEquals( - new Collection([null, $webhook1->id, $webhook2->id]), + new Collection([$webhook1->id, $webhook2->id]), $this->mapper->mapToWebhooks($flowMeasure)->map(fn (WebhookInterface $webhook) => $webhook->id()) ); }