Skip to content
Commits on Source (6)
......@@ -18,8 +18,8 @@ class Install extends Cli\Controller implements Interfaces\CliControllerInterfac
{
$this->out('Configures web server and provisions and sets up databases for the minds application.');
$this->out('use-existing-settings: uses the existing settings in settings.php.');
$this->out('only=[keys|site|cassandra|cockroach] to set up individual components.');
$this->out('cleanCassandra cleanCockroach: deletes and recreates db.');
$this->out('only=[keys|site|cassandra|] to set up individual components.');
$this->out('cleanCassandra: deletes and recreates db.');
$this->out('graceful-storage-provision: causes installation to proceed past storage (db) failures.');
}
......@@ -52,7 +52,7 @@ class Install extends Cli\Controller implements Interfaces\CliControllerInterfac
$provisioner->checkOptions();
$this->out('OK');
// only=[keys|cassandra|cockroach|site]
// only=[keys|cassandra|site]
$installOnly = $this->getopt('only');
$installType = $installOnly ? $installOnly : "all";
......@@ -75,17 +75,6 @@ class Install extends Cli\Controller implements Interfaces\CliControllerInterfac
$this->out('Something BAD happened while provisioning Cassandra' . $ex->getMessage());
}
try {
if ($installType == "all" || $installType == "cockroach") {
$this->out('- Provisioning Cockroach:');
$isCleanCockroach = $this->getopt("cleanCockroach") != null;
$provisioner->provisionCockroach(null, $isCleanCockroach);
$this->out('OK');
}
} catch (Exception $ex) {
$this->out('Something BAD happened while provisioning Cockroach' . $ex->getMessage());
}
if (($installType == "all") || ($installType == "site")) {
$this->out('- Setting up site:', $this::OUTPUT_INLINE);
$provisioner->setupSite();
......
<?php
namespace Minds\Core\Email\CampaignLogs;
use Minds\Traits\MagicAttributes;
/**
* @method int getReceiverGuid()
* @method CampaignLog setReceiverGuid(int $receiverGuid)
* @method int getTimeSent()
* @method CampaignLog setTimeSet(int $timeSent)
* @method string getEmailCampaignId()
* @method CampaignLog setEmailCampaignId(string $emailCampaignId)
*/
class CampaignLog
{
use MagicAttributes;
/** @var int $receiverGuid the user guid who received the email */
protected $receiverGuid;
/** @var int $timeStamp the timestamp when the email was sent */
protected $timeSent;
/** @var string $emailCampaignId the class name of the email campaign */
protected $emailCampaignID;
}
<?php
/**
* Email Campaign Log Repository.
*/
namespace Minds\Core\Email\CampaignLogs;
use Minds\Core\Data\Cassandra\Client;
use Minds\Core\Di\Di;
use Cassandra\Varint;
use Cassandra\Timestamp;
use Minds\Core\Data\Cassandra\Prepared\Custom;
use Minds\Core\Email\CampaignLogs\CampaignLog;
class Repository
{
/**
* @var Client
*/
protected $db;
/**
* Repository constructor.
*
* @param null $db
*/
public function __construct($db = null)
{
$this->db = $db ?: Di::_()->get('Database\Cassandra\Cql');
}
/**
* Gets the list of email states, they are clustered on time_sent, so they will be in descending order.
*
* @param array $options
*
* receiver_guid the user guid
* limit 10
*
* @return CampaignLog[]
*/
public function getList(array $options = [])
{
$options = array_merge([
'limit' => 10,
'offset' => '',
'receiver_guid' => null,
], $options);
$template = 'SELECT * FROM email_campaign_logs';
$where = "";
$values = [];
if (isset($options['receiver_guid'])) {
$where = 'receiver_guid = ?';
$values[] = new Varint($options['receiver_guid']);
}
if ($where) {
$template .= ' WHERE ' . $where;
}
$query = new Custom();
$query->query($template, $values);
$query->setOpts([
'page_size' => (int) $options['limit'],
'paging_state_token' => $options['offset'],
]);
try {
$result = $this->db->request($query);
} catch (\Exception $e) {
error_log($e);
}
$campaignLog = [];
$token = '';
if ($result) {
foreach ($result as $row) {
$campaignLog = (new CampaignLog())
->setReceiverGuid($row['receiver_guid']->value())
->setTimeSent($row['time_sent'])
->setEmailCampaignId($row['email_campaign_id']);
$campaignLogs[] = $campaignLog;
}
$token = base64_encode($result->pagingStateToken());
}
return [
'data' => $campaignLogs,
'next' => $token,
];
}
/**
* Inserts an email campaign log into cassandra.
*
* @param CampaignLog $campaignLog
*
* @return bool the write results
*
* @throws \Exception
*/
public function add(CampaignLog $campaignLog)
{
$template = 'INSERT INTO email_campaign_logs (receiver_guid, time_sent, email_campaign_id) VALUES (?, ?, ?)';
$values = [
new Varint($campaignLog->getReceiverGuid()),
new Timestamp($campaignLog->getTimeSent()),
(string) $campaignLog->getEmailCampaignId(),
];
$query = new Custom();
$query->query($template, $values);
return $this->db->request($query);
}
}
......@@ -7,6 +7,7 @@ use Minds\Core\Email\EmailSubscription;
use Minds\Core\Email\Manager;
use Minds\Entities\User;
use Minds\Traits\MagicAttributes;
use Minds\Core\Email\CampaignLogs\CampaignLog;
abstract class EmailCampaign
{
......@@ -65,4 +66,31 @@ abstract class EmailCampaign
return true;
}
/**
* Returns the short name of the class as the template name.
*/
public function getEmailCampaignId()
{
return (new \ReflectionClass($this))->getShortName();
}
/**
* Saves when the user received the email campaign to the db.
*
* @var int defaults to the current time
*/
public function saveCampaignLog(int $time = null)
{
$time = $time ?: time();
if (!$this->manager || !$this->user) {
return false;
}
$campaignLog = (new CampaignLog())
->setReceiverGuid($this->user->guid)
->setTimeSent($time)
->setEmailCampaignId($this->getEmailCampaignId());
$this->manager->saveCampaignLog($campaignLog);
}
}
......@@ -70,11 +70,13 @@ class GoneCold extends EmailCampaign
return $message;
}
public function send()
{
public function send($time = null)
{
$time = $time ?: time();
//send email
if ($this->canSend()) {
$this->mailer->queue($this->build());
$this->saveCampaignLog($time);
}
}
}
......@@ -70,11 +70,13 @@ class WelcomeComplete extends EmailCampaign
return $message;
}
public function send()
public function send($time = null)
{
$time = $time ?: time();
//send email
if ($this->canSend()) {
$this->mailer->queue($this->build());
$this->saveCampaignLog($time);
}
}
}
......@@ -75,6 +75,7 @@ class WelcomeIncomplete extends EmailCampaign
//send email
if ($this->canSend()) {
$this->mailer->queue($this->build());
$this->saveCampaignLog();
}
}
}
<?php
namespace Minds\Core\Email\Delegates;
use Minds\Core\Suggestions\Manager;
use Minds\Entities\User;
use Minds\Core\Di\Di;
use Minds\Interfaces\SenderInterface;
use Minds\Core\Email\Campaigns\UserRetention\GoneCold;
class GoneColdSender implements SenderInterface
{
/** @var Manager */
private $manager;
/** @var GoneCold */
private $campaign;
public function __construct(Manager $manager = null, GoneCold $campaign = null)
{
$this->manager = $manager ?: Di::_()->get('Suggestions\Manager');
$this->campaign = $campaign ?: new GoneCold();
}
public function send(User $user)
{
$this->manager->setUser($user);
$suggestions = $this->manager->getList();
$this->campaign->setUser($user);
$this->campaign->setSuggestions($suggestions);
$this->campaign->send();
}
}
<?php
namespace Minds\Core\Email\Delegates;
use Minds\Entities\User;
use Minds\Core\Di\Di;
use Minds\Core\Suggestions\Manager as SuggestionsManager;
use Minds\Core\Onboarding\Manager as OnboardingManager;
use Minds\Interfaces\SenderInterface;
use Minds\Core\Email\Campaigns\UserRetention\WelcomeComplete;
use Minds\Core\Email\Campaigns\UserRetention\WelcomeIncomplete;
class WelcomeSender implements SenderInterface
{
/** @var SuggestionsManager */
private $suggestionsManager;
/** @var OnboardingManager */
private $onboardingManager;
/** @var WelcomeComplete */
private $welcomeComplete;
/** @var WelcomeIncomplete */
private $welcomeIncomplete;
public function __construct(
SuggestionsManager $suggestionsManager = null,
OnboardingManager $onboardingManager = null,
WelcomeComplete $welcomeComplete = null,
WelcomeIncomplete $welcomeIncomplete = null
) {
$this->suggestionsManager = $suggestionsManager ?: Di::_()->get('Suggestions\Manager');
$this->onboardingManager = $onboardingManager ?: Di::_()->get('Onboarding\Manager');
$this->welcomeComplete = $welcomeComplete ?: new WelcomeComplete();
$this->welcomeIncomplete = $welcomeIncomplete ?: new WelcomeIncomplete();
}
public function send(User $user)
{
$this->onboardingManager->setUser($user);
$campaign = $this->welcomeComplete;
if ($this->onboardingManager->isComplete()) {
$this->suggestionsManager->setUser($user);
$suggestions = $this->suggestionsManager->getList();
$campaign->setSuggestions($suggestions);
} else {
$campaign = $this->welcomeIncomplete;
}
$campaign->setUser($user);
$campaign->send();
}
}
......@@ -8,11 +8,13 @@ namespace Minds\Core\Email;
use Minds\Core\Di\Di;
use Minds\Core\Events\Dispatcher;
use Minds\Core\Analytics\UserStates\UserActivityBuckets;
use Minds\Core\Email\Campaigns\UserRetention\GoneCold;
use Minds\Core\Email\Campaigns\UserRetention\WelcomeComplete;
use Minds\Core\Email\Campaigns\UserRetention\WelcomeIncomplete;
use Minds\Entities\User;
use Minds\Core\Email\Manager;
use Minds\Core\Suggestions\Manager as SuggestionManager;
use Minds\Interfaces\SenderInterface;
class Events
{
......@@ -36,6 +38,7 @@ class Events
Dispatcher::register('user_state_change', UserActivityBuckets::STATE_NEW, function ($opts) {
error_log('user_state_change new');
$this->sendCampaign(new Delegates\WelcomeSender(), $opts->getParameters());
});
Dispatcher::register('user_state_change', UserActivityBuckets::STATE_RESURRECTED, function ($opts) {
......@@ -43,37 +46,17 @@ class Events
});
Dispatcher::register('user_state_change', UserActivityBuckets::STATE_COLD, function ($opts) {
error_log('user_state_change cold');
$params = $opts->getParameters();
$user = new User($params['user_guid']);
$manager = new SuggestionManager();
$manager->setUser($user);
$suggestions = $manager->getList();
$campaign = (new GoneCold())
->setUser($user)
->setSuggestions($suggestions);
$campaign->send();
$this->sendCampaign(new Delegates\GoneColdSender(), $opts->getParameters());
});
Dispatcher::register('welcome_email', 'all', function ($opts) {
error_log('welcome_email');
$params = $opts->getParameters();
$user = new User($params['user_guid']);
$onboardingManager = Di::_()->get('Onboarding\Manager');
$onboardingManager->setUser($user);
if ($onboardingManager->isComplete()) {
$campaign = (new WelcomeComplete());
$suggestionManager = Di::_()->get('Suggestions\Manager');
$suggestionManager->setUser($user);
$suggestions = $suggestionManager->getList();
$campaign->setSuggestions($suggestions);
} else {
$campaign = (new WelcomeIncomplete());
error_log('Sending Welcome Incomplete');
}
$campaign->setUser($user);
$campaign->send();
$this->sendCampaign(new Delegates\WelcomeSender(), $opts->getParameters());
});
}
private function sendCampaign (SenderInterface $sender, $params) {
$user = new User($params['user_guid']);
$sender->send($user);
}
}
......@@ -7,15 +7,24 @@ namespace Minds\Core\Email;
use Minds\Core\Di\Di;
use Minds\Core\Email\EmailSubscription;
use Minds\Core\Entities;
use Minds\Entities\User;
use Minds\Core\Email\Repository;
use Minds\Core\Email\CampaignLogs\Repository as CampaignLogsRepository;
use Minds\Core\Email\CampaignLogs\CampaignLog;
class Manager
{
/** @var Repository */
protected $repository;
public function __construct($repository = null)
/** @var CampaignLogsRepository */
protected $campaignLogsRepository;
public function __construct(Repository $repository = null, CampaignLogsRepository $campaignLogsRepository = null)
{
$this->repository = $repository ?: Di::_()->get('Email\Repository');
$this->campaignLogsRepository = $campaignLogsRepository ?: Di::_()->get('Email\CampaignLogs\Repository');
}
public function getSubscribers($options = [])
......@@ -108,4 +117,20 @@ class Manager
return true;
}
/**
* Saves a log when we send a user a campaign email
* Used to select subsequent mailings and send different emails
* @param CampaignLog $campaignLog the receiver, time and campaign class name
* @return boolean the add result
*/
public function saveCampaignLog(CampaignLog $campaignLog) {
$this->campaignLogsRepository->add($campaignLog);
}
public function getCampaignLogs(User $receiver) {
$options = [
'receiver_guid' => $receiver->guid
];
return $this->campaignLogsRepository->getList($options);
}
}
......@@ -37,5 +37,9 @@ class Provider extends DiProvider
$this->di->bind('Email\EmailStyles', function ($di) {
return new EmailStyles();
}, ['useFactory' => false]);
$this->di->bind('Email\CampaignLogs\Repository', function ($di) {
return new CampaignLogs\Repository();
}, ['useFactory' => true]);
}
}
......@@ -228,11 +228,9 @@ class Installer
}
public function setupStorage(Provisioners\ProvisionerInterface $cassandraStorage = null,
Provisioners\ProvisionerInterface $cockroachProvisioner = null,
$cleanData = false)
{
$this->provisionCassandra($cassandraStorage, $cleanData);
$this->provisionCockroach($cockroachProvisioner, $cleanData);
}
public function provisionCassandra(Provisioners\ProvisionerInterface $cassandraStorage = null,
......@@ -242,13 +240,6 @@ class Installer
$cassandraStorage->provision($cleanData);
}
public function provisionCockroach(Provisioners\ProvisionerInterface $cockroachProvisioner = null,
$cleanData = false)
{
$cockroachProvisioner = $cockroachProvisioner ?: new Provisioners\CockroachProvisioner();
$cockroachProvisioner->provision($cleanData);
}
public function reloadStorage()
{
Core\Data\Pool::$pools = [];
......
<?php
namespace Minds\Core\Provisioner\Provisioners;
use Minds\Core\Di\Di;
use PDO;
class CockroachProvisioner implements ProvisionerInterface
{
protected $config;
public function provision(bool $cleanData)
{
$config = Di::_()->get('Config')->get('database');
$host = isset($config['host']) ? $config['host'] : 'cockroachdb';
$port = isset($config['port']) ? $config['port'] : 26257;
$dbName = isset($config['name']) ? $config['name'] : 'minds';
$sslMode = isset($config['sslmode']) ? $config['sslmode'] : 'disable';
$username = isset($config['username']) ? $config['username'] : 'php';
// Using root account because only superusers have permission to create databases.
$adminDb = new PDO("pgsql:host=$host;port=$port;dbname=$dbName;sslmode=$sslMode",
'root',
null,
[
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_EMULATE_PREPARES => true,
PDO::ATTR_PERSISTENT => true,
]);
$adminDb->prepare("CREATE USER IF NOT EXISTS $username")->execute();
if ($cleanData)
{
$adminDb->prepare("DROP DATABASE IF EXISTS $dbName")->execute();
}
$adminDb->prepare("CREATE DATABASE IF NOT EXISTS $dbName")->execute();
$adminDb->prepare("GRANT ALL ON DATABASE $dbName TO $username")->execute();
$schema = explode(';', file_get_contents(dirname(__FILE__) . '/cockroach-provision.sql'));
foreach ($schema as $query) {
if (trim($query) === '') {
continue;
}
try {
$statement = $adminDb->prepare($query);
$statement->execute();
} catch (\Exception $ex) {
error_log("Error running cockroach statement: " . $ex->getMessage());
}
}
}
}
......@@ -1429,3 +1429,10 @@ CREATE TABLE minds.user_snapshots (
json_data text,
PRIMARY KEY (user_guid, type, key)
) WITH CLUSTERING ORDER BY (type ASC, key ASC);
CREATE TABLE minds.email_campaign_logs (
receiver_guid varint,
time_sent timestamp,
email_campaign_id text,
PRIMARY KEY (receiver_guid, time_sent)
) WITH CLUSTERING ORDER BY (time_sent desc);
CREATE TABLE minds.comments (
uuid UUID NOT NULL DEFAULT gen_random_uuid(),
legacy_guid INT NULL,
parent_uuid UUID NULL,
owner_guid INT NULL,
entity_guid INT NULL,
created_timestamp TIMESTAMP NULL DEFAULT now():::TIMESTAMP,
notification_type STRING(20) NULL,
data JSONB NULL,
CONSTRAINT "primary" PRIMARY KEY (uuid ASC),
INDEX legacy_guid_idx (legacy_guid ASC),
FAMILY "primary" (uuid, legacy_guid, parent_uuid, owner_guid, entity_guid, created_timestamp, notification_type, data)
);
CREATE TABLE minds.entities (
uuid UUID NOT NULL DEFAULT gen_random_uuid(),
legacy_guid INT NULL,
owner_guid INT NULL,
entity_guid INT NULL,
created_timestamp TIMESTAMP NULL DEFAULT now():::TIMESTAMP,
CONSTRAINT "primary" PRIMARY KEY (uuid ASC),
INDEX legacy_guid_idx (legacy_guid ASC),
FAMILY "primary" (uuid, legacy_guid, owner_guid, entity_guid, created_timestamp)
);
CREATE TABLE minds.entity_hashtags (
guid INT NOT NULL,
hashtag STRING NOT NULL,
CONSTRAINT "primary" PRIMARY KEY (guid ASC, hashtag ASC),
INDEX entity_hashtags_hashtag_idx (hashtag ASC),
INDEX entity_hashtags_hashtag_guid_idx (hashtag ASC, guid ASC),
FAMILY "primary" (guid, hashtag)
);
CREATE TABLE minds.helpdesk_categories (
uuid UUID NOT NULL DEFAULT gen_random_uuid(),
title STRING(100) NOT NULL,
parent UUID NULL,
branch STRING NULL,
CONSTRAINT "primary" PRIMARY KEY (uuid ASC),
FAMILY "primary" (uuid, title, parent, branch)
);
CREATE TABLE minds.helpdesk_faq (
uuid UUID NOT NULL DEFAULT gen_random_uuid(),
question STRING NULL,
answer STRING NULL,
category_uuid UUID NULL,
CONSTRAINT "primary" PRIMARY KEY (uuid ASC),
CONSTRAINT fk_category_uuid_ref_helpdesk_categories FOREIGN KEY (category_uuid) REFERENCES helpdesk_categories (uuid),
INDEX helpdesk_faq_auto_index_fk_category_uuid_ref_helpdesk_categories (category_uuid ASC),
FAMILY "primary" (uuid, question, answer, category_uuid)
);
CREATE TABLE minds.helpdesk_votes (
question_uuid UUID NOT NULL,
user_guid STRING(18) NOT NULL,
direction STRING NOT NULL,
CONSTRAINT "primary" PRIMARY KEY (question_uuid ASC, user_guid ASC, direction ASC),
FAMILY "primary" (question_uuid, user_guid, direction)
);
CREATE TABLE minds.hidden_hashtags (
hashtag STRING NOT NULL,
hidden_since TIMESTAMP NOT NULL DEFAULT now(),
admin_guid INT NOT NULL,
CONSTRAINT "primary" PRIMARY KEY (hashtag ASC),
FAMILY "primary" (hashtag, hidden_since, admin_guid)
);
CREATE TABLE minds.notification_batches (
user_guid INT NOT NULL,
batch_id STRING NOT NULL,
CONSTRAINT "primary" PRIMARY KEY (user_guid ASC, batch_id ASC),
INDEX notification_batches_batch_id_idx (batch_id ASC),
FAMILY "primary" (user_guid, batch_id)
);
CREATE TABLE minds.notifications (
uuid UUID NOT NULL DEFAULT gen_random_uuid(),
to_guid INT NOT NULL,
from_guid INT NULL,
created_timestamp TIMESTAMP NULL DEFAULT now():::TIMESTAMP,
read_timestamp TIMESTAMP NULL,
notification_type STRING NOT NULL,
data JSONB NULL,
entity_guid STRING NULL,
batch_id STRING NULL,
CONSTRAINT "primary" PRIMARY KEY (to_guid ASC, notification_type ASC, uuid DESC),
INDEX notifications_redux_created_timestamp_idx (created_timestamp DESC),
INDEX notifications_redux_batch_id_idx (batch_id ASC) STORING (from_guid, entity_guid, created_timestamp, read_timestamp, data),
FAMILY "primary" (uuid, to_guid, from_guid, created_timestamp, read_timestamp, notification_type, data, entity_guid, batch_id)
);
CREATE TABLE minds.suggested (
type STRING NOT NULL,
guid INT NOT NULL,
rating INT NULL,
score INT NULL,
lastsynced TIMESTAMP NULL,
CONSTRAINT "primary" PRIMARY KEY (type ASC, guid ASC),
INDEX suggested_lastsynced_score_rating_idx (lastsynced DESC, score DESC, rating DESC),
INDEX suggested_lastsynced_score_idx (lastsynced DESC, score DESC),
INDEX suggested_redux_rating_idx (rating DESC) STORING (lastsynced, score),
FAMILY "primary" (type, guid, rating, score, lastsynced)
);
CREATE TABLE minds.suggested_tags (
guid INT NOT NULL,
rating INT NULL,
type STRING NULL,
score INT NULL,
lastsynced TIMESTAMP NULL,
hashtags STRING[] NULL,
CONSTRAINT "primary" PRIMARY KEY (guid ASC),
FAMILY "primary" (guid, rating, type, score, lastsynced, hashtags)
);
<?php
namespace Minds\Interfaces;
use Minds\Entities\User;
/**
* Delegate interface for sending emails
*/
interface SenderInterface
{
/**
* sending campaign emails to a user
* @return void
*/
public function send(User $user);
}
\ No newline at end of file
<?php
namespace Spec\Minds\Core\Email\CampaignLogs;
use Minds\Core\Data\Cassandra\Client;
use Minds\Core\Email\CampaignLogs\Repository;
use Minds\Core\Email\CampaignLogs\CampaignLog;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
use Spec\Minds\Mocks\Cassandra\Rows;
use Cassandra\Varint;
class RepositorySpec extends ObjectBehavior
{
protected $db;
public function let(Client $db)
{
$this->db = $db;
$this->beConstructedWith($db);
}
public function it_is_initializable()
{
$this->shouldHaveType(Repository::class);
}
public function it_should_write_a_log ()
{
$campaignLog = (new CampaignLog())
->setReceiverGuid(123)
->setTimeSent(0)
->setEmailCampaignId('test');
$this->db->request(Argument::that(function ($query) {
$built = $query->build();
return $built['string'] === 'INSERT INTO email_campaign_logs (receiver_guid, time_sent, email_campaign_id) VALUES (?, ?, ?)';
}))
->shouldBeCalled()
->willReturn(true);
$this->add($campaignLog)->shouldReturn(true);
}
public function it_should_get_a_list_of_logs()
{
$opts = [
'receiver_guid' => 123,
'limit' => 10,
'offset' => '',
];
$this->db->request(Argument::that(function ($query) {
$built = $query->build();
return $built['string'] === 'SELECT * FROM email_campaign_logs WHERE receiver_guid = ?';
}))
->shouldBeCalled()
->willReturn(new Rows([
[
'receiver_guid' => new Varint(123),
'time_sent' => 1,
'email_campaign_id' => 'test'
],
[
'receiver_guid' => new Varint(123),
'time_sent' => 1,
'email_campaign_id' => 'test2'
],
], ''));
$this->getList($opts)->shouldBeArray();
}
}
......@@ -8,6 +8,7 @@ use Minds\Core\Email\Mailer;
use Minds\Core\Email\Manager;
use Minds\Core\Email\EmailSubscription;
use Minds\Core\Suggestions\Suggestion;
use Minds\Core\Email\CampaignLogs\CampaignLog;
use Minds\Entities\User;
use Prophecy\Argument;
......@@ -57,15 +58,24 @@ class GoneColdSpec extends ObjectBehavior
$data['username']->shouldEqual($this->testUsername);
$this->mailer->queue(Argument::any())->shouldBeCalled();
$testEmailSubscription = (new EmailSubscription())
->setUserGuid($this->testGUID)
->setCampaign('global')
->setTopic('minds_tips')
->setValue(true);
$time = time();
$campaignLog = (new CampaignLog())
->setReceiverGuid($this->testGUID)
->setTimeSent($time)
->setEmailCampaignId($this->getEmailCampaignId()->getWrappedObject());
$this->manager->saveCampaignLog($campaignLog)->shouldBeCalled();
$this->manager->isSubscribed($testEmailSubscription)->shouldBeCalled()->willReturn(true);
$this->send();
$this->send($time);
}
public function it_should_not_send_unsubscribed(User $user)
......
......@@ -8,6 +8,7 @@ use Minds\Core\Email\Mailer;
use Minds\Core\Email\Manager;
use Minds\Core\Email\EmailSubscription;
use Minds\Core\Suggestions\Suggestion;
use Minds\Core\Email\CampaignLogs\CampaignLog;
use Minds\Entities\User;
use Prophecy\Argument;
......@@ -66,6 +67,15 @@ class WelcomeCompleteSpec extends ObjectBehavior
->setTopic('minds_tips')
->setValue(true);
$time = time();
$campaignLog = (new CampaignLog())
->setReceiverGuid($this->testGUID)
->setTimeSent($time)
->setEmailCampaignId($this->getEmailCampaignId()->getWrappedObject());
$this->manager->saveCampaignLog($campaignLog)->shouldBeCalled();
$this->manager->isSubscribed($testEmailSubscription)->shouldBeCalled()->willReturn(true);
$this->send();
}
......