Skip to content

Commit b2a5d5f

Browse files
committed
Add Redact API
1 parent d6a10bd commit b2a5d5f

File tree

11 files changed

+250
-1
lines changed

11 files changed

+250
-1
lines changed

src/Client.php

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ public function __construct(CredentialsInterface $credentials, $options = array(
102102
'conversion' => 'Nexmo\Conversion\Client',
103103
'conversation' => 'Nexmo\Conversations\Collection',
104104
'user' => 'Nexmo\User\Collection',
105+
'redact' => 'Nexmo\Redact\Client',
105106
], $this));
106107
}
107108

@@ -187,14 +188,21 @@ public static function signRequest(RequestInterface $request, SignatureSecret $c
187188
public static function authRequest(RequestInterface $request, Basic $credentials)
188189
{
189190
switch($request->getHeaderLine('content-type')){
190-
case 'application/json':
191+
case 'application/json':
192+
if (static::requiresAuthInUrlNotBody($request)) {
193+
$query = [];
194+
parse_str($request->getUri()->getQuery(), $query);
195+
$query = array_merge($query, $credentials->asArray());
196+
$request = $request->withUri($request->getUri()->withQuery(http_build_query($query)));
197+
} else {
191198
$body = $request->getBody();
192199
$body->rewind();
193200
$content = $body->getContents();
194201
$params = json_decode($content, true);
195202
$params = array_merge($params, $credentials->asArray());
196203
$body->rewind();
197204
$body->write(json_encode($params));
205+
}
198206
break;
199207
case 'application/x-www-form-urlencoded':
200208
$body = $request->getBody();
@@ -447,6 +455,14 @@ public function __get($name)
447455
return $this->factory->getApi($name);
448456
}
449457

458+
protected static function requiresAuthInUrlNotBody(\Psr\Http\Message\RequestInterface $request)
459+
{
460+
$path = $request->getUri()->getPath();
461+
$isRedactEndpoint = strpos($path, '/v1/redact') === 0;
462+
463+
return $isRedactEndpoint;
464+
}
465+
450466
protected function needsKeypairAuthentication(\Psr\Http\Message\RequestInterface $request)
451467
{
452468
$path = $request->getUri()->getPath();

src/Redact/Client.php

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
<?php
2+
3+
namespace Nexmo\Redact;
4+
5+
use Nexmo\Client\ClientAwareInterface;
6+
use Nexmo\Client\ClientAwareTrait;
7+
use Nexmo\Network;
8+
use Psr\Http\Message\ResponseInterface;
9+
use Zend\Diactoros\Request;
10+
use Nexmo\Client\Exception;
11+
12+
13+
class Client implements ClientAwareInterface
14+
{
15+
use ClientAwareTrait;
16+
17+
public function transaction($id, $product, $options = [])
18+
{
19+
$request = new Request(
20+
\Nexmo\Client::BASE_API . '/v1/redact/transaction',
21+
'POST',
22+
'php://temp',
23+
[
24+
'Accept' => 'application/json',
25+
'Content-Type' => 'application/json'
26+
]
27+
);
28+
29+
$body = ['id' => $id, 'product' => $product] + $options;
30+
31+
$request->getBody()->write(json_encode($body));
32+
$response = $this->client->send($request);
33+
34+
$rawBody = $response->getBody()->getContents();
35+
36+
if('204' != $response->getStatusCode()){
37+
throw $this->getException($response);
38+
}
39+
40+
return null;
41+
}
42+
43+
protected function getException(ResponseInterface $response)
44+
{
45+
$response->getBody()->rewind();
46+
$body = json_decode($response->getBody()->getContents(), true);
47+
$status = $response->getStatusCode();
48+
49+
$msg = 'Unexpected error';
50+
51+
// This is an error at the load balancer, likely auth related
52+
if (isset($body['error_title'])) {
53+
$msg = $body['error_title'];
54+
}
55+
56+
if (isset($body['title'])) {
57+
$msg = $body['title'];
58+
if (isset($body['detail'])) {
59+
$msg .= ' - '.$body['detail'];
60+
}
61+
62+
$msg .= '. See '.$body['type'];
63+
}
64+
65+
if($status >= 400 AND $status < 500) {
66+
$e = new Exception\Request($msg, $status);
67+
} elseif($status >= 500 AND $status < 600) {
68+
$e = new Exception\Server($msg, $status);
69+
} else {
70+
$e = new Exception\Exception('Unexpected HTTP Status Code');
71+
throw $e;
72+
}
73+
74+
return $e;
75+
}
76+
77+
78+
}

test/Redact/ClientTest.php

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
<?php
2+
/**
3+
* Nexmo Client Library for PHP
4+
*
5+
* @copyright Copyright (c) 2016 Nexmo, Inc. (http://nexmo.com)
6+
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
7+
*/
8+
9+
namespace NexmoTest\Redact;
10+
11+
use Nexmo\Redact\Client;
12+
use Zend\Diactoros\Response;
13+
use NexmoTest\Psr7AssertionTrait;
14+
use Prophecy\Argument;
15+
use Psr\Http\Message\RequestInterface;
16+
use PHPUnit\Framework\TestCase;
17+
use Nexmo\Client\Exception;
18+
19+
class ClientTest extends TestCase
20+
{
21+
use Psr7AssertionTrait;
22+
23+
protected $nexmoClient;
24+
25+
/**
26+
* @var Client
27+
*/
28+
protected $redact;
29+
30+
public function setUp()
31+
{
32+
$this->nexmoClient = $this->prophesize('Nexmo\Client');
33+
$this->redact = new Client();
34+
$this->redact->setClient($this->nexmoClient->reveal());
35+
}
36+
37+
public function testUrlAndMethod()
38+
{
39+
$this->nexmoClient->send(Argument::that(function (RequestInterface $request) {
40+
$this->assertEquals('/v1/redact/transaction', $request->getUri()->getPath());
41+
$this->assertEquals('api.nexmo.com', $request->getUri()->getHost());
42+
$this->assertEquals('POST', $request->getMethod());
43+
return true;
44+
}))->shouldBeCalledTimes(1)->willReturn($this->getResponse('success', 204));
45+
46+
$this->redact->transaction('ABC123', 'sms');
47+
}
48+
49+
public function testNoOptions()
50+
{
51+
$this->nexmoClient->send(Argument::that(function (RequestInterface $request) {
52+
$this->assertRequestJsonBodyContains('id', 'ABC123', $request);
53+
$this->assertRequestJsonBodyContains('product', 'sms', $request);
54+
55+
return true;
56+
}))->shouldBeCalledTimes(1)->willReturn($this->getResponse('success', 204));
57+
58+
$this->redact->transaction('ABC123', 'sms');
59+
}
60+
61+
public function testWithOptions()
62+
{
63+
$this->nexmoClient->send(Argument::that(function (RequestInterface $request) {
64+
$this->assertRequestJsonBodyContains('id', 'ABC123', $request);
65+
$this->assertRequestJsonBodyContains('product', 'sms', $request);
66+
$this->assertRequestJsonBodyContains('type', 'inbound', $request);
67+
return true;
68+
}))->shouldBeCalledTimes(1)->willReturn($this->getResponse('success', 204));
69+
70+
$this->redact->transaction('ABC123', 'sms', ['type' => 'inbound']);
71+
}
72+
73+
public function testOptionsDoNotOverwriteParams()
74+
{
75+
$this->nexmoClient->send(Argument::that(function (RequestInterface $request) {
76+
$this->assertRequestJsonBodyContains('id', 'ABC123', $request);
77+
$this->assertRequestJsonBodyContains('product', 'sms', $request);
78+
$this->assertRequestJsonBodyContains('type', 'inbound', $request);
79+
return true;
80+
}))->shouldBeCalledTimes(1)->willReturn($this->getResponse('success', 204));
81+
82+
$this->redact->transaction('ABC123', 'sms', ['id' => 'ZZZ', 'type' => 'inbound']);
83+
}
84+
85+
/**
86+
* @dataProvider exceptionsProvider
87+
*/
88+
public function testExceptions($response, $code, $expectedException, $expectedMessage)
89+
{
90+
$this->setExpectedException($expectedException, $expectedMessage);
91+
92+
$this->nexmoClient->send(Argument::that(function (RequestInterface $request) {
93+
return true;
94+
}))->shouldBeCalledTimes(1)->willReturn($this->getResponse($response, $code));
95+
96+
$this->redact->transaction('ABC123', 'sms');
97+
}
98+
99+
public function exceptionsProvider() {
100+
return [
101+
'unauthorized' => ['unauthorized', 401, Exception\Request::class, "Unauthorized"],
102+
'premature-redaction' => ['premature-redaction', 403, Exception\Request::class, "Premature Redaction - You must wait 60 minutes before redacting ID '0A000000B0C9A1234'. See https://developer.nexmo.com/api-errors/redact#premature-redaction"],
103+
'unprovisioned' => ['unprovisioned', 403, Exception\Request::class, "Authorisation error - User=ABC123 is not provisioned to redact product=SMS. See https://developer.nexmo.com/api-errors#unprovisioned"],
104+
'invalid-id' => ['invalid-id', 404, Exception\Request::class, "Invalid ID - ID '0A000000B0C9A1234' could not be found (type=MT). See https://developer.nexmo.com/api-errors#invalid-id"],
105+
'invalid-json' => ['invalid-json', 422, Exception\Request::class, "Invalid JSON - Unexpected character ('\"' (code 34)): was expecting comma to separate Object entries. See https://developer.nexmo.com/api-errors#invalid-json"],
106+
'unsupported-product' => ['unsupported-product', 422, Exception\Request::class, "Invalid Product - No product corresponding to supplied string sms2!. See https://developer.nexmo.com/api-errors/redact#invalid-product"],
107+
'unknown-error' => ['error', 500, Exception\Server::class, "Unexpected error"],
108+
];
109+
}
110+
111+
/**
112+
* Get the API response we'd expect for a call to the API.
113+
*
114+
* @param string $type
115+
* @return Response
116+
*/
117+
protected function getResponse($type = 'success', $status = 200)
118+
{
119+
return new Response(fopen(__DIR__ . '/responses/' . $type . '.json', 'r'), $status);
120+
}
121+
}

test/Redact/responses/error.json

Whitespace-only changes.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"type": "https://developer.nexmo.com/api-errors#invalid-id",
3+
"title": "Invalid ID",
4+
"detail": "ID '0A000000B0C9A1234' could not be found (type=MT)",
5+
"instance": "bf0ca0bf927b3b52e3cb03217e1a1ddf"
6+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"type": "https://developer.nexmo.com/api-errors#invalid-json",
3+
"title": "Invalid JSON",
4+
"detail": "Unexpected character ('\"' (code 34)): was expecting comma to separate Object entries",
5+
"instance": "bf0ca0bf927b3b52e3cb03217e1a1ddf"
6+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"type": "https://developer.nexmo.com/api-errors/redact#premature-redaction",
3+
"title": "Premature Redaction",
4+
"detail": "You must wait 60 minutes before redacting ID '0A000000B0C9A1234'",
5+
"instance": "bf0ca0bf927b3b52e3cb03217e1a1ddf"
6+
}

test/Redact/responses/success.json

Whitespace-only changes.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"type": "UNAUTHORIZED",
3+
"error_title": "Unauthorized"
4+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"type": "https://developer.nexmo.com/api-errors#unprovisioned",
3+
"title": "Authorisation error",
4+
"detail": "User=ABC123 is not provisioned to redact product=SMS",
5+
"instance": "bf0ca0bf927b3b52e3cb03217e1a1ddf"
6+
}

0 commit comments

Comments
 (0)