APIv4 - Add is_draft field for mailings, fix test
authorcolemanw <coleman@civicrm.org>
Sat, 4 Nov 2023 03:28:55 +0000 (23:28 -0400)
committercolemanw <coleman@civicrm.org>
Sun, 5 Nov 2023 15:45:31 +0000 (10:45 -0500)
Figuring out if a mailing is a draft or not can be tricky.
This encapsulates the tricky-ness in a reusable calculated field.

Civi/Api4/Service/Spec/Provider/MailingGetSpecProvider.php
tests/phpunit/api/v4/Entity/MailingEventTest.php [moved from tests/phpunit/api/v4/Entity/MailingEvent.php with 75% similarity]

index c66482debd312982fa196d1f495ef399c20fa651..9a10292a8c9f8fc9e4b55a3bb8657ea081f42388 100644 (file)
@@ -12,6 +12,7 @@
 
 namespace Civi\Api4\Service\Spec\Provider;
 
+use Civi\Api4\Query\Api4SelectQuery;
 use Civi\Api4\Service\Spec\FieldSpec;
 use Civi\Api4\Service\Spec\RequestSpec;
 
@@ -27,6 +28,14 @@ class MailingGetSpecProvider extends \Civi\Core\Service\AutoService implements G
    * @throws \CRM_Core_Exception
    */
   public function modifySpec(RequestSpec $spec): void {
+    $field = new FieldSpec('is_draft', 'Mailing', 'Boolean');
+    $field->setLabel(ts('Is Draft'))
+      ->setDescription(ts('True if mailing has not been scheduled or sent'))
+      ->setColumnName('id')
+      ->setReadonly(TRUE)
+      ->setSqlRenderer([__CLASS__, 'isDraft']);
+    $spec->addFieldSpec($field);
+
     $field = new FieldSpec('stats_intended_recipients', 'Mailing', 'Integer');
     $field->setLabel(ts('Stats: Intended Recipients'))
       ->setDescription(ts('Total emails sent'))
@@ -134,6 +143,19 @@ class MailingGetSpecProvider extends \Civi\Core\Service\AutoService implements G
     return $entity === 'Mailing' && $action === 'get';
   }
 
+  /**
+   * Check if mailing has not been scheduled by anyone and has no record in MailingJob table.
+   *
+   * @param array $idField
+   * @param \Civi\Api4\Query\Api4SelectQuery $query
+   * @return string
+   */
+  public static function isDraft(array $idField, Api4SelectQuery $query): string {
+    $id = $idField['sql_name'];
+    $scheduled_id = $query->getFieldSibling($idField, 'scheduled_id')['sql_name'];
+    return "IF($scheduled_id OR EXISTS (SELECT 1 FROM civicrm_mailing_job WHERE is_test = 0 AND mailing_id = $id), 0, 1)";
+  }
+
   /**
    * Generate SQL for counting mailing events
    *
similarity index 75%
rename from tests/phpunit/api/v4/Entity/MailingEvent.php
rename to tests/phpunit/api/v4/Entity/MailingEventTest.php
index d0b2db70eef11cdb2d4f379ffe917c8c0e9b7741..f626c4a74b644556112f591b5e5287d948a4dbc9 100644 (file)
@@ -19,6 +19,7 @@
 namespace api\v4\Entity;
 
 use api\v4\Api4TestBase;
+use Civi\Api4\Mailing;
 use Civi\Test\TransactionalInterface;
 
 /**
@@ -33,29 +34,53 @@ class MailingEventTest extends Api4TestBase implements TransactionalInterface {
     $eid2 = $this->createTestRecord('Email', ['contact_id' => $cid2])['id'];
     $mid1 = $this->createTestRecord('Mailing')['id'];
     $mid2 = $this->createTestRecord('Mailing')['id'];
-    $parentJobIDs = $this->saveTestRecords('MailingJob',
-      [
-        'records' => [
-          ['mailing_id' => $mid1, 'is_test' => 'false'],
-          ['mailing_id' => $mid2, 'is_test' => 'false'],
-        ],
-      ])->column('id');
+    $parentJobIDs = $this->saveTestRecords('MailingJob', [
+      'records' => [
+        ['mailing_id' => $mid1, 'is_test' => FALSE],
+        ['mailing_id' => $mid2, 'is_test' => TRUE],
+      ],
+    ])->column('id');
+
+    // Test is_draft field
+    $mailings = Mailing::get(FALSE)
+      ->addSelect('id', 'is_draft')
+      ->addWhere('id', 'IN', [$mid1, $mid2])
+      ->execute()->indexBy('id');
+    $this->assertFalse($mailings[$mid1]['is_draft']);
+    $this->assertTrue($mailings[$mid2]['is_draft']);
+
+    Mailing::update(FALSE)
+      ->addWhere('id', '=', $mid2)
+      ->addValue('scheduled_id', $cid1)
+      ->execute();
+
+    $mailings = Mailing::get(FALSE)
+      ->addSelect('id', 'is_draft')
+      ->addWhere('id', 'IN', [$mid1, $mid2])
+      ->execute()->indexBy('id');
+    $this->assertFalse($mailings[$mid1]['is_draft']);
+    $this->assertFalse($mailings[$mid2]['is_draft']);
+
+    $parentJobIDs[] = $this->createTestRecord('MailingJob', [
+      'mailing_id' => $mid2,
+      'is_test' => FALSE,
+    ])['id'];
 
     $childJobIDs = $this->saveTestRecords('MailingJob',
       [
         'records' => [
           ['mailing_id' => $mid1, 'parent_id' => $parentJobIDs[0], 'job_type' => 'child', 'is_test' => 'false'],
-          ['mailing_id' => $mid2, 'parent_id' => $parentJobIDs[1], 'job_type' => 'child', 'is_test' => 'false'],
+          ['mailing_id' => $mid2, 'parent_id' => $parentJobIDs[2], 'job_type' => 'child', 'is_test' => 'false'],
         ],
       ])->column('id');
 
     $queueIDs = $this->saveTestRecords('MailingEventQueue',
       [
         'records' => [
-          ['job_id' => $childJobIDs[0], 'contact_id' => $cid1, 'email_id' => $eid1],
-          ['job_id' => $childJobIDs[0], 'contact_id' => $cid2, 'email_id' => $eid2],
-          ['job_id' => $childJobIDs[1], 'contact_id' => $cid1, 'email_id' => $eid1],
-          ['job_id' => $childJobIDs[1], 'contact_id' => $cid2, 'email_id' => $eid2],
+          ['job_id' => $childJobIDs[0], 'mailing_id' => $mid1, 'contact_id' => $cid1, 'email_id' => $eid1],
+          ['job_id' => $childJobIDs[0], 'mailing_id' => $mid1, 'contact_id' => $cid2, 'email_id' => $eid2],
+          ['job_id' => $childJobIDs[1], 'mailing_id' => $mid2, 'contact_id' => $cid1, 'email_id' => $eid1],
+          ['job_id' => $childJobIDs[1], 'mailing_id' => $mid2, 'contact_id' => $cid2, 'email_id' => $eid2],
         ],
       ])->column('id');
 
@@ -108,10 +133,10 @@ class MailingEventTest extends Api4TestBase implements TransactionalInterface {
         ],
       ]);
 
-    $mailings = \Civi\Api4\Mailing::get(FALSE)
+    $mailings = Mailing::get(FALSE)
       ->addSelect('stats_intended_recipients', 'stats_successful', 'stats_opens_total', 'stats_opens_unique',
         'stats_clicks_total', 'stats_clicks_unique', 'stats_bounces', 'stats_unsubscribes', 'stats_optouts',
-        'stats_optouts_and_unsubscribes', 'stats_forwards', 'stats_replies')
+        'stats_optouts_and_unsubscribes', 'stats_forwards', 'stats_replies', 'is_draft')
       ->addWhere('id', 'IN', [$mid1, $mid2])
       ->addOrderBy('id', 'ASC')
       ->execute();
@@ -140,6 +165,8 @@ class MailingEventTest extends Api4TestBase implements TransactionalInterface {
     $this->assertEquals(0, $mailings[1]['stats_optouts']);
     $this->assertEquals(2, $mailings[0]['stats_optouts_and_unsubscribes']);
     $this->assertEquals(0, $mailings[1]['stats_optouts_and_unsubscribes']);
+    $this->assertFalse($mailings[0]['is_draft']);
+    $this->assertFalse($mailings[1]['is_draft']);
   }
 
 }