Skip to content

Commit 0a399c2

Browse files
committed
Allow Verfications to be 'Acted' Upon:
- If the verification entity has a client, can check, cancel, trigger, and sync data without using the client. - This implements spec that API methods "May also be represented as methods of an entity object returned by the client." - Creates pattern where: - Nexmo\Client represents HTTP communication / endpoints with the API - Nexmo\Verify\Client reperesents APIs as methods. - Nexmo\Verify\Verification represents the API concept. - This pattern allows `Verification::check()` to return a bool, while `Client::check()` matches the APIs errors with exceptions.
1 parent 26a997c commit 0a399c2

File tree

4 files changed

+209
-0
lines changed

4 files changed

+209
-0
lines changed

src/Verify/Client.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,8 @@ protected function checkError(Verification $verification, $data)
146146

147147
protected function processReqRes(Verification $verification, RequestInterface $req, ResponseInterface $res, $replace = true)
148148
{
149+
$verification->setClient($this);
150+
149151
if($replace || !$verification->getRequest()){
150152
$verification->setRequest($req);
151153
}

src/Verify/Verification.php

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
namespace Nexmo\Verify;
1010

11+
use Nexmo\Client\Exception\Request as RequestException;
1112
use Nexmo\Entity\JsonResponseTrait;
1213
use Nexmo\Entity\Psr7Trait;
1314
use Nexmo\Entity\RequestArrayTrait;
@@ -28,6 +29,11 @@ class Verification implements VerificationInterface, \ArrayAccess
2829

2930
protected $dirty = true;
3031

32+
/**
33+
* @var Client;
34+
*/
35+
protected $client;
36+
3137
/**
3238
* Create a verification with a number and brand, or the `request_id` of an existing verification.
3339
*
@@ -48,6 +54,83 @@ public function __construct($number, $brand = null, $additional = [])
4854
}
4955
}
5056

57+
/**
58+
* Allow Verification to have actions.
59+
*
60+
* @param Client $client Verify Client
61+
* @return $this
62+
*/
63+
public function setClient(Client $client)
64+
{
65+
$this->client = $client;
66+
return $this;
67+
}
68+
69+
/**
70+
* @return Client
71+
*/
72+
protected function useClient()
73+
{
74+
if(isset($this->client)){
75+
return $this->client;
76+
}
77+
78+
throw new \RuntimeException('can not act on the verification directly unless a verify client has been set');
79+
}
80+
81+
/**
82+
* Check if the code is correct. Unlike the method it proxies, an invalid code does not throw an exception.
83+
*
84+
* @uses \Nexmo\Verify\Client::check()
85+
* @param string $code Numeric code provided by the user.
86+
* @param null|string $ip IP address to be used for the verification.
87+
* @return bool Code is valid.
88+
* @throws RequestException
89+
*/
90+
public function check($code, $ip = null)
91+
{
92+
try {
93+
$this->useClient()->check($this, $code, $ip);
94+
return true;
95+
} catch(RequestException $e) {
96+
if($e->getCode() == 16 || $e->getCode() == 17){
97+
return false;
98+
}
99+
100+
throw $e;
101+
}
102+
}
103+
104+
/**
105+
* Cancel the verification.
106+
*
107+
* @uses \Nexmo\Verify\Client::cancel()
108+
*/
109+
public function cancel()
110+
{
111+
$this->useClient()->cancel($this);
112+
}
113+
114+
/**
115+
* Trigger the next verification.
116+
*
117+
* @uses \Nexmo\Verify\Client::trigger()
118+
*/
119+
public function trigger()
120+
{
121+
$this->useClient()->trigger($this);
122+
}
123+
124+
/**
125+
* Update Verification from the API.
126+
*
127+
* @uses \Nexmo\Verify\Client::search()
128+
*/
129+
public function sync()
130+
{
131+
$this->useClient()->search($this);
132+
}
133+
51134
/**
52135
* Check if the user provided data has sent to the API yet.
53136
*

test/Verify/ClientTest.php

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,39 @@ public function setUp()
3737
$this->client->setClient($this->nexmoClient->reveal());
3838
}
3939

40+
/**
41+
* @dataProvider getApiMethods
42+
*/
43+
public function testClientSetsSelf($method, $response, $construct, $args = [])
44+
{
45+
$client = $this->prophesize('Nexmo\Client');
46+
$client->send(Argument::cetera())->willReturn($this->getResponse($response));
47+
48+
$this->client->setClient($client->reveal());
49+
50+
$mock = $this->getMockBuilder('Nexmo\Verify\Verification')
51+
->setConstructorArgs($construct)
52+
->setMethods(['setClient'])
53+
->getMock();
54+
55+
$mock->expects($this->once())->method('setClient')->with($this->client);
56+
57+
array_unshift($args, $mock);
58+
call_user_func_array([$this->client, $method], $args);
59+
}
60+
61+
public function getApiMethods()
62+
{
63+
return [
64+
['start', 'start', ['14845551212', 'Test Verify']],
65+
['cancel', 'cancel', ['44a5279b27dd4a638d614d265ad57a77']],
66+
['trigger', 'trigger', ['44a5279b27dd4a638d614d265ad57a77']],
67+
['search', 'search', ['44a5279b27dd4a638d614d265ad57a77']],
68+
['check', 'check', ['44a5279b27dd4a638d614d265ad57a77'], ['1234']],
69+
];
70+
}
71+
72+
4073
public function testCanStartVerification()
4174
{
4275
$success = $this->setupClientForStart('start');

test/Verify/VerificationTest.php

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
use Nexmo\Verify\Check;
1212
use Nexmo\Verify\Verification;
13+
use Prophecy\Argument;
1314
use Zend\Diactoros\Response;
1415

1516
class VerificationTest extends \PHPUnit_Framework_TestCase
@@ -189,6 +190,96 @@ public function dataResponses()
189190
];
190191
}
191192

193+
/**
194+
* @dataProvider getClientProxyMethods
195+
*/
196+
public function testMethodsProxyClient($method, $proxy, $code = null, $ip = null)
197+
{
198+
$client = $this->prophesize('Nexmo\Verify\Client');
199+
if(!is_null($ip)){
200+
$prediction = $client->$proxy($this->exsisting, $code, $ip);
201+
} elseif(!is_null($code)){
202+
$prediction = $client->$proxy($this->exsisting, $code, Argument::cetera());
203+
} else {
204+
$prediction = $client->$proxy($this->exsisting);
205+
}
206+
207+
$prediction->shouldBeCalled()->willReturn($this->exsisting);
208+
209+
$this->exsisting->setClient($client->reveal());
210+
211+
if(!is_null($ip)){
212+
$this->exsisting->$method($code, $ip);
213+
} elseif(!is_null($code)){
214+
$this->exsisting->$method($code);
215+
} else {
216+
$this->exsisting->$method();
217+
}
218+
}
219+
220+
public function testCheckReturnsBoolForInvalidCode()
221+
{
222+
$client = $this->prophesize('Nexmo\Verify\Client');
223+
$client->check($this->exsisting, '1234', Argument::cetera())->willReturn($this->exsisting);
224+
$client->check($this->exsisting, '4321', Argument::cetera())->willThrow(new \Nexmo\Client\Exception\Request('dummy', '16'));
225+
226+
$this->exsisting->setClient($client->reveal());
227+
228+
$this->assertFalse($this->exsisting->check('4321'));
229+
$this->assertTrue($this->exsisting->check('1234'));
230+
}
231+
232+
public function testCheckReturnsBoolForTooManyAttempts()
233+
{
234+
$client = $this->prophesize('Nexmo\Verify\Client');
235+
$client->check($this->exsisting, '1234', Argument::cetera())->willReturn($this->exsisting);
236+
$client->check($this->exsisting, '4321', Argument::cetera())->willThrow(new \Nexmo\Client\Exception\Request('dummy', '17'));
237+
238+
$this->exsisting->setClient($client->reveal());
239+
240+
$this->assertFalse($this->exsisting->check('4321'));
241+
$this->assertTrue($this->exsisting->check('1234'));
242+
}
243+
244+
public function testExceptionForCheckFail()
245+
{
246+
$client = $this->prophesize('Nexmo\Verify\Client');
247+
$client->check($this->exsisting, '1234', Argument::cetera())->willReturn($this->exsisting);
248+
$client->check($this->exsisting, '4321', Argument::cetera())->willThrow(new \Nexmo\Client\Exception\Request('dummy', '6'));
249+
250+
$this->exsisting->setClient($client->reveal());
251+
252+
$this->expectException('Nexmo\Client\Exception\Request');
253+
$this->exsisting->check('4321');
254+
}
255+
256+
/**
257+
* @dataProvider getClientProxyMethods
258+
*/
259+
public function testMissingClientException($method, $proxy, $code = null, $ip = null)
260+
{
261+
$this->expectException('RuntimeException');
262+
263+
if(!is_null($ip)){
264+
$this->exsisting->$method($code, $ip);
265+
} elseif(!is_null($code)){
266+
$this->exsisting->$method($code);
267+
} else {
268+
$this->exsisting->$method();
269+
}
270+
}
271+
272+
public function getClientProxyMethods()
273+
{
274+
return [
275+
['cancel', 'cancel'],
276+
['trigger', 'trigger'],
277+
['sync', 'search'],
278+
['check', 'check', '1234'],
279+
['check', 'check', '1234', '192.168.1.1'],
280+
];
281+
}
282+
192283
/**
193284
* Get the API response we'd expect for a call to the API. Verify API currently returns 200 all the time, so only
194285
* change between success / fail is body of the message.

0 commit comments

Comments
 (0)