Skip to content
Commits on Source (2)
......@@ -80,8 +80,7 @@ class referrals implements Interfaces\Api
$prospect_guid = $pages[0];
$referral = new Referral;
$referral->setReferrerGuid($referrer_guid)
->setProspectGuid($prospect_guid)
->setPingTimestamp(time());
->setProspectGuid($prospect_guid);
$manager = Di::_()->get('Referrals\Manager');
if(!$manager->ping($referral)){
......
......@@ -95,7 +95,22 @@ class Manager
*/
public function ping($referral)
{
// Ensure ping is triggered by referrer
$urn = $referral->getUrn();
$response = $this->repository->get($urn);
// No response if repo finds no matches for incoming referral/prospect combo
if (!$response) {
return false;
}
// Don't ping if prospect isn't pingable
$pingable = $response->getPingable();
if (!$pingable) {
return false;
}
// Update ping_timestamp
$referral->setPingTimestamp(time());
$this->repository->ping($referral);
// Send a ping notification to the prospect
......
......@@ -64,24 +64,39 @@ class Referral
*/
public function getPingable()
{
// Disable ping if prospect has already joined rewards
if ($this->joinTimestamp) {
return false;
}
// Duration referrer must wait before re-pinging (in seconds)
$waitTime= 60*60*24*7; // 7 days
$waitTime = 60*60*24*7; // 7 days
$now = time();
$elapsedTime = $now - $this->pingTimestamp;
// Not enough time has elapsed
if ($this->pingTimestamp && $elapsedTime < $waitTime) {
return false;
}
// Also disable ping if they've already joined rewards
if ($this->joinTimestamp) {
return false;
}
return true;
}
/**
* Return the URN of this referral
* @return string
*/
public function getUrn()
{
$parts = [
$this->getReferrerGuid(),
$this->getProspectGuid(),
];
return "urn:referral:" . implode('-', $parts);
}
/**
* Export
* @return array
......@@ -95,7 +110,8 @@ class Referral
'pingable' => $this->getPingable(),
'register_timestamp' => $this->registerTimestamp * 1000,
'join_timestamp' => $this->joinTimestamp * 1000,
'ping_timestamp' => $this->pingTimestamp * 1000
'ping_timestamp' => $this->pingTimestamp * 1000,
'urn' => $this->getUrn(),
];
}
}
......@@ -9,21 +9,55 @@ use Minds\Core\Di\Di;
use Minds\Core\Data\Cassandra\Prepared;
use Cassandra;
use Cassandra\Bigint;
use Minds\Common\Urn;
class Repository
{
/** @var Client $client */
private $client;
public function __construct($client = null)
/** @var Urn $urn */
protected $urn;
public function __construct($client = null, $urn = null)
{
$this->client = $client ?: Di::_()->get('Database\Cassandra\Cql');
$this->urn = $urn ?: new Urn;
}
/**
* Return a single referral
* @param string $urn
* @return Referral
*/
public function get($urn)
{
$parts = explode('-', $this->urn->setUrn($urn)->getNss());
if (!$parts[0] && !$parts[1]) {
return null;
}
$referrerGuid = $parts[0];
$prospectGuid = $parts[1];
$response = $this->getList([
'referrer_guid' => $referrerGuid,
'prospect_guid' => $prospectGuid,
]);
if (!$response[0]) {
return null;
}
return $response[0];
}
/**
* Return a list of referrals
* @param array $opts
* @param array $opts 'limit', 'offset', 'referrer_guid', 'prospect_guid'
* @return Response
* @throws \Exception
*/
public function getList($opts = [])
{
......@@ -31,12 +65,28 @@ class Repository
'limit' => 12,
'offset' => '',
'referrer_guid' => null,
'prospect_guid' => null,
], $opts);
if (!$opts['referrer_guid']) {
throw new \Exception('Referrer GUID is required');
}
$response = new Response;
$statement = "SELECT * FROM referrals";
$where = ["referrer_guid = ?"];
$values = [new Bigint($opts['referrer_guid'])];
if ($opts['prospect_guid']) {
$where[] = "prospect_guid = ?";
$values[] = new Bigint($opts['prospect_guid']);
}
$statement .= " WHERE " . implode(' AND ', $where);
$cqlOpts = [];
if ($opts['limit']) {
$cqlOpts['page_size'] = (int) $opts['limit'];
......@@ -46,15 +96,10 @@ class Repository
$cqlOpts['paging_state_token'] = base64_decode($opts['offset']);
}
$template = "SELECT * FROM referrals WHERE referrer_guid = ?";
$values = [ new Bigint($opts['referrer_guid']) ];
$query = new Prepared\Custom();
$query->query($template, $values);
$query->query($statement, $values);
$query->setOpts($cqlOpts);
$response = new Response();
try {
$rows = $this->client->request($query);
......@@ -87,6 +132,7 @@ class Repository
* Add a referral
* @param Referral $referral
* @return bool
* @throws \Exception
*/
public function add(Referral $referral)
{
......@@ -130,6 +176,7 @@ class Repository
* Update a referral when the prospect joins rewards program
* @param Referral $referral
* @return bool
* @throws \Exception
*/
public function update(Referral $referral)
{
......@@ -169,6 +216,7 @@ class Repository
* Update referral when prospect is notified by the referrer to urge them to join rewards
* @param Referral $referral
* @return bool
* @throws \Exception
*/
public function ping(Referral $referral)
{
......
......@@ -182,8 +182,6 @@ class Join
->setAction('referral')
->push();
// TODO: give prospect +50 contribution score as well
$this->referralDelegate->onReferral($this->user);
}
}
......
......@@ -64,12 +64,23 @@ class ManagerSpec extends ObjectBehavior
->shouldReturn(true);
}
function it_should_update_ping_timestamp_and_trigger_ping_notification()
function it_should_ping_if_pingable()
{
$response = new Referral();
$response->setReferrerGuid(456)
->setProspectGuid(123)
->setPingTimestamp(111)
->setRegisterTimestamp(11)
->setJoinTimestamp(null);
$referral = new Referral();
$referral->setProspectGuid(123)
->setReferrerGuid(456)
->setPingTimestamp(111);
->setPingTimestamp(111); // Pingable because >7 days has passed since 111
$this->repository->get($referral->getUrn())
->shouldBeCalled()
->willReturn($response);
$this->repository->ping($referral)
->shouldBeCalled();
$this->notificationDelegate->notifyProspect($referral)
......@@ -78,6 +89,48 @@ class ManagerSpec extends ObjectBehavior
->shouldReturn(true);
}
function it_should_not_ping_during_ping_waiting_period()
{
$referral = new Referral();
$referral->setProspectGuid(123)
->setReferrerGuid(456)
->setPingTimestamp(32503742874); // Jan 1, 3000; !pingable because ping timestamp is not less than now
$this->repository->get($referral->getUrn())
->shouldBeCalled()
->willReturn(null);
$this->repository->ping($referral)
->shouldNotBeCalled();
$this->notificationDelegate->notifyProspect($referral)
->shouldNotBeCalled();
$this->ping($referral)
->shouldReturn(false);
}
function it_should_not_ping_if_current_user_is_not_referrer()
{
$response = new Response();
$response[] = (new Referral)
->setReferrerGuid(789)
->setProspectGuid(123)
->setPingTimestamp(111)
->setRegisterTimestamp(11)
->setJoinTimestamp(null);
$referral = new Referral();
$referral->setProspectGuid(123)
->setReferrerGuid(456)
->setPingTimestamp(111); // Pingable because >7 days has passed since 111
$this->repository->get($referral->getUrn())
->shouldBeCalled()
->willReturn(null);
$this->repository->ping($referral)
->shouldNotBeCalled();
$this->notificationDelegate->notifyProspect($referral)
->shouldNotBeCalled();
$this->ping($referral)
->shouldReturn(false);
}
function it_should_get_a_list_of_referrals()
{
$response = new Response();
......@@ -102,10 +155,10 @@ class ManagerSpec extends ObjectBehavior
->willReturn((new User)->set('guid', 456));
$newResponse = $this->getList([
'limit' => 12,
'offset' => '',
'referrer_guid' => 123,
'hydrate' => true
'limit' => 12,
'offset' => '',
'referrer_guid' => 123,
'hydrate' => true
]);
$newResponse[0]->getReferrerGuid()
......
......@@ -6,8 +6,10 @@ use Minds\Common\Repository\Response;
use Minds\Core\Referrals\Referral;
use Minds\Core\Referrals\Repository;
use Minds\Core\Data\Cassandra\Client;
use Minds\Common\Urn;
use Cassandra\Bigint;
use Cassandra\Timestamp;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
use Spec\Minds\Mocks\Cassandra\Rows;
......@@ -18,11 +20,13 @@ class RepositorySpec extends ObjectBehavior
{
private $client;
protected $urn;
function let(Client $client)
function let(Client $client, Urn $urn)
{
$this->beConstructedWith($client);
$this->beConstructedWith($client, $urn);
$this->client = $client;
$this->urn = $urn;
}
function it_is_initializable()
......@@ -59,7 +63,6 @@ class RepositorySpec extends ObjectBehavior
->shouldBe(true);
}
function it_should_update_a_referral()
{
$referral = new Referral();
......@@ -72,6 +75,47 @@ class RepositorySpec extends ObjectBehavior
->shouldReturn(true);
}
function it_should_return_a_single_referral()
{
$this->urn->setUrn('urn:referral:123-456')
->shouldBeCalled()
->willReturn($this->urn);
$this->urn->getNss()
->shouldBeCalled()
->willReturn('123-456');
$this->client->request(Argument::that(function($prepared) {
return true;
}))
->shouldBeCalled()
->willReturn(new Rows([
[
'referrer_guid' => new Bigint(123),
'prospect_guid' => new Bigint(456),
'register_timestamp' => new Timestamp(1545451597777),
'join_timestamp' => null,
'ping_timestamp' => null,
],
], 'my-cool-paging-token'));
$response = $this->get('urn:referral:123-456');
$response->getReferrerGuid()
->shouldBe('123');
$response->getProspectGuid()
->shouldBe('456');
$response->getRegisterTimestamp()
->shouldBe(1545451597777);
$response->getJoinTimestamp()
->shouldBe(null);
$response->getPingTimestamp()
->shouldBe(null);
}
function it_should_return_a_list_of_referrals()
{
$this->client->request(Argument::that(function($prepared) {
......@@ -107,11 +151,8 @@ class RepositorySpec extends ObjectBehavior
->shouldBe(1545451597777);
$response[0]->getJoinTimestamp()
->shouldBe(1545451597778);
}
function it_should_throw_if_no_referrer_guid_during_get_list()
{
$opts = [
......