add exceptions, fix auth, implement response, sanitize recipient and sender
This commit is contained in:
parent
0cdfd0185b
commit
50cf733101
48
.github/workflows/code-checks.yml
vendored
Normal file
48
.github/workflows/code-checks.yml
vendored
Normal file
@ -0,0 +1,48 @@
|
||||
name: CodeChecks
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
php:
|
||||
- '7.3'
|
||||
- '7.4'
|
||||
- '8.0'
|
||||
|
||||
name: PHP ${{ matrix.php }} tests
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Set up PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: ${{ matrix.php }}
|
||||
coverage: xdebug
|
||||
|
||||
- name: Cache Composer packages
|
||||
id: composer-cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: vendor
|
||||
key: ${{ runner.os }}-php${{ matrix.php }}-${{ hashFiles('**/composer.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-php${{ matrix.php }}-
|
||||
|
||||
- name: Composer
|
||||
run: composer install --no-progress
|
||||
|
||||
- name: PHPUnit
|
||||
run: vendor/bin/phpunit --coverage-clover=coverage.xml
|
||||
|
||||
- name: "Upload coverage to Codecov"
|
||||
uses: "codecov/codecov-action@v2"
|
||||
with:
|
||||
fail_ci_if_error: true
|
@ -19,7 +19,7 @@
|
||||
],
|
||||
"require": {
|
||||
"php": "^7.0",
|
||||
"beberlei/assert": "^2.7",
|
||||
"beberlei/assert": "^2.9",
|
||||
"guzzlehttp/guzzle": "~6.2",
|
||||
"psr/http-message": "~1.0",
|
||||
"symfony/options-resolver": "^3.0|^4.0",
|
||||
|
@ -15,7 +15,10 @@
|
||||
|
||||
namespace D3\LinkmobilityClient;
|
||||
|
||||
use D3\LinkmobilityClient\Exceptions\ApiException;
|
||||
use D3\LinkmobilityClient\Request\RequestInterface;
|
||||
use GuzzleHttp\RequestOptions;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
class Client
|
||||
{
|
||||
@ -34,10 +37,11 @@ class Client
|
||||
$this->requestClient = $client ?: new \GuzzleHttp\Client( [ 'base_uri' => $this->apiUrl->getBaseUri() ] );
|
||||
}
|
||||
|
||||
public function request(RequestInterface $request) : ResponseInterface
|
||||
public function request(RequestInterface $request) : \D3\LinkmobilityClient\Response\ResponseInterface
|
||||
{
|
||||
$request->validate();
|
||||
$responseClass = $request->getResponseClass();
|
||||
|
||||
return $request->getResponseInstance(
|
||||
$this->rawRequest($request->getUri(), $request->getMethod(), $request->getOptions())
|
||||
);
|
||||
@ -48,22 +52,14 @@ class Client
|
||||
* @param string $method
|
||||
* @param array $postArgs
|
||||
*
|
||||
* @return string
|
||||
* @return ResponseInterface
|
||||
* @throws ApiException
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
protected function rawRequest( $url, string $method = RequestInterface::METHOD_GET, array $options = []): string
|
||||
protected function rawRequest( $url, string $method = RequestInterface::METHOD_GET, array $options = []): ResponseInterface
|
||||
{
|
||||
$options['headers']['Authorization'] = 'access_token '.$this->accessToken;
|
||||
|
||||
if (!empty($body)) {
|
||||
$options['json'] = $body;
|
||||
}
|
||||
dumpvar(__METHOD__.__LINE__.PHP_EOL);
|
||||
dumpvar($options);
|
||||
dumpVar($method);
|
||||
dumpvar($url);
|
||||
die();
|
||||
$options['headers']['Authorization'] = 'Bearer '.$this->accessToken;
|
||||
|
||||
$response = $this->requestClient->request(
|
||||
$method,
|
||||
$url,
|
||||
|
21
src/Exceptions/ApiException.php
Normal file
21
src/Exceptions/ApiException.php
Normal file
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This Software is the property of Data Development and is protected
|
||||
* by copyright law - it is NOT Freeware.
|
||||
* Any unauthorized use of this software without a valid license
|
||||
* is a violation of the license agreement and will be prosecuted by
|
||||
* civil and criminal law.
|
||||
* http://www.shopmodule.com
|
||||
*
|
||||
* @copyright (C) D3 Data Development (Inh. Thomas Dartsch)
|
||||
* @author D3 Data Development - Daniel Seifert <support@shopmodule.com>
|
||||
* @link http://www.oxidmodule.com
|
||||
*/
|
||||
|
||||
namespace D3\LinkmobilityClient\Exceptions;
|
||||
|
||||
class ApiException extends LinkmobilityException
|
||||
{
|
||||
|
||||
}
|
23
src/Exceptions/LinkmobilityException.php
Normal file
23
src/Exceptions/LinkmobilityException.php
Normal file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This Software is the property of Data Development and is protected
|
||||
* by copyright law - it is NOT Freeware.
|
||||
* Any unauthorized use of this software without a valid license
|
||||
* is a violation of the license agreement and will be prosecuted by
|
||||
* civil and criminal law.
|
||||
* http://www.shopmodule.com
|
||||
*
|
||||
* @copyright (C) D3 Data Development (Inh. Thomas Dartsch)
|
||||
* @author D3 Data Development - Daniel Seifert <support@shopmodule.com>
|
||||
* @link http://www.oxidmodule.com
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace D3\LinkmobilityClient\Exceptions;
|
||||
|
||||
class LinkmobilityException extends \Exception
|
||||
{
|
||||
|
||||
}
|
23
src/Exceptions/RecipientException.php
Normal file
23
src/Exceptions/RecipientException.php
Normal file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This Software is the property of Data Development and is protected
|
||||
* by copyright law - it is NOT Freeware.
|
||||
* Any unauthorized use of this software without a valid license
|
||||
* is a violation of the license agreement and will be prosecuted by
|
||||
* civil and criminal law.
|
||||
* http://www.shopmodule.com
|
||||
*
|
||||
* @copyright (C) D3 Data Development (Inh. Thomas Dartsch)
|
||||
* @author D3 Data Development - Daniel Seifert <support@shopmodule.com>
|
||||
* @link http://www.oxidmodule.com
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace D3\LinkmobilityClient\Exceptions;
|
||||
|
||||
class RecipientException extends LinkmobilityException
|
||||
{
|
||||
|
||||
}
|
@ -18,6 +18,7 @@ declare(strict_types=1);
|
||||
namespace D3\LinkmobilityClient\RecipientsList;
|
||||
|
||||
use D3\LinkmobilityClient\ValueObject\Recipient;
|
||||
use libphonenumber\PhoneNumberType;
|
||||
|
||||
class RecipientsList implements RecipientsListInterface, \Iterator
|
||||
{
|
||||
@ -26,14 +27,33 @@ class RecipientsList implements RecipientsListInterface, \Iterator
|
||||
*/
|
||||
private $recipients = [];
|
||||
|
||||
public function add(Recipient $recipient)
|
||||
public function add(Recipient $recipient) : RecipientsListInterface
|
||||
{
|
||||
$this->recipients[md5(serialize($recipient))] = $recipient;
|
||||
$phoneUtil = \libphonenumber\PhoneNumberUtil::getInstance();
|
||||
try {
|
||||
$phoneNumber = $phoneUtil->parse($recipient->get(), $recipient->getCountryCode());
|
||||
|
||||
if (false === $phoneUtil->isValidNumber($phoneNumber)) {
|
||||
throw new \D3\LinkmobilityClient\Exceptions\RecipientException('invalid recipient phone number');
|
||||
} elseif (false === in_array($phoneUtil->getNumberType($phoneNumber), [PhoneNumberType::MOBILE, PhoneNumberType::FIXED_LINE_OR_MOBILE])) {
|
||||
throw new \D3\LinkmobilityClient\Exceptions\RecipientException('not a mobile number');
|
||||
}
|
||||
|
||||
$this->recipients[md5(serialize($recipient))] = $recipient;
|
||||
} catch (\libphonenumber\NumberParseException $e) {
|
||||
// var_dump($e);
|
||||
} catch (\D3\LinkmobilityClient\Exceptions\RecipientException $e) {
|
||||
// var_dump($e);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function clearRecipents()
|
||||
public function clearRecipents() : RecipientsListInterface
|
||||
{
|
||||
$this->recipients = [];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getRecipients() : array
|
||||
|
@ -21,9 +21,9 @@ use D3\LinkmobilityClient\ValueObject\Recipient;
|
||||
|
||||
interface RecipientsListInterface
|
||||
{
|
||||
public function add(Recipient $recipient);
|
||||
public function add(Recipient $recipient) : RecipientsListInterface;
|
||||
|
||||
public function clearRecipents();
|
||||
public function clearRecipents() : RecipientsListInterface;
|
||||
|
||||
public function getRecipients() : array;
|
||||
}
|
@ -25,6 +25,7 @@ use D3\LinkmobilityClient\ValueObject\Recipient;
|
||||
use D3\LinkmobilityClient\ValueObject\Sender;
|
||||
use D3\LinkmobilityClient\ValueObject\SmsMessage;
|
||||
use D3\LinkmobilityClient\ValueObject\StringValueObject;
|
||||
use GuzzleHttp\RequestOptions;
|
||||
use OxidEsales\Eshop\Core\Registry;
|
||||
|
||||
abstract class Request implements RequestInterface
|
||||
@ -125,7 +126,8 @@ abstract class Request implements RequestInterface
|
||||
Assert::that($this->getOptions())->isArray();
|
||||
|
||||
Assert::that( $this->getRecipientsList() )->isInstanceOf(RecipientsList::class)->notEmpty();
|
||||
Assert::thatAll( $this->getRecipientsList() )->isInstanceOf( Recipient::class );
|
||||
Assert::that( $this->getRecipientsList()->getRecipients())->notEmpty();
|
||||
Assert::thatAll( $this->getRecipientsList() )->isInstanceOf( Recipient::class )->notEmpty();
|
||||
|
||||
// optional properties
|
||||
Assert::thatNullOr( $this->getClientMessageId() )->string();
|
||||
@ -151,10 +153,10 @@ abstract class Request implements RequestInterface
|
||||
'priority' => $this->getPriority(),
|
||||
'recipientAddressList' => $this->getRecipientsList()->getRecipients(),
|
||||
'sendAsFlashSms' => $this->getSendAsFlashSms(),
|
||||
'senderAddress' => $this->getSenderAddress()->get(),
|
||||
'senderAddress' => $this->getSenderAddress() ? $this->getSenderAddress()->get() : null,
|
||||
'senderAddressType' => $this->getSenderAddressType(),
|
||||
'test' => $this->isTest(),
|
||||
'validityPeriode' => $this->getValidityPeriode()
|
||||
'validityPeriode' => $this->getValidityPeriode()
|
||||
];
|
||||
}
|
||||
|
||||
@ -166,7 +168,7 @@ abstract class Request implements RequestInterface
|
||||
|
||||
switch ($this->getContentType()) {
|
||||
case RequestInterface::CONTENTTYPE_JSON:
|
||||
return ['json' => json_encode($body)];
|
||||
return [RequestOptions::JSON => $body];
|
||||
default:
|
||||
return $body;
|
||||
}
|
||||
@ -180,7 +182,6 @@ abstract class Request implements RequestInterface
|
||||
'Accept' => $this->contentType,
|
||||
'Content-Type' => $this->contentType,
|
||||
]
|
||||
|
||||
],
|
||||
$this->getBody()
|
||||
);
|
||||
@ -382,9 +383,9 @@ abstract class Request implements RequestInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
* @return Sender|null
|
||||
*/
|
||||
public function getSenderAddress() : Sender
|
||||
public function getSenderAddress()
|
||||
{
|
||||
return $this->senderAddress;
|
||||
}
|
||||
@ -444,6 +445,7 @@ abstract class Request implements RequestInterface
|
||||
*/
|
||||
public function getResponseInstance(\Psr\Http\Message\ResponseInterface $rawResponse): ResponseInterface
|
||||
{
|
||||
return new $this->getResponseClass($rawResponse);
|
||||
$FQClassName = $this->getResponseClass();
|
||||
return new $FQClassName($rawResponse);
|
||||
}
|
||||
}
|
@ -8,26 +8,78 @@ use Assert\Assert;
|
||||
|
||||
abstract class Response implements ResponseInterface
|
||||
{
|
||||
const STATUSCODE = 'statusCode';
|
||||
const STATUSMESSAGE = 'statusMessage';
|
||||
const CLIENTMESSAGEID = 'clientMessageId';
|
||||
const TRANSFERID = 'transferId';
|
||||
const SMSCOUNT = 'smsCount';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
* @var \Psr\Http\Message\ResponseInterface
|
||||
*/
|
||||
protected $data;
|
||||
protected $rawResponse;
|
||||
|
||||
protected $content;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $status;
|
||||
|
||||
public function __construct(array $data)
|
||||
public function __construct(\Psr\Http\Message\ResponseInterface $rawResponse)
|
||||
{
|
||||
Assert::that($data)->keyExists('status');
|
||||
$this->rawResponse = $rawResponse;
|
||||
$this->content = json_decode($this->rawResponse->getBody()->getContents(), true);
|
||||
}
|
||||
|
||||
public function getRawResponse() : \Psr\Http\Message\ResponseInterface
|
||||
{
|
||||
return $this->rawResponse;
|
||||
}
|
||||
|
||||
$this->data = $data;
|
||||
$this->status = (int)$this->data['status'];
|
||||
public function getContent()
|
||||
{
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
if ($this->isSuccessful()) {
|
||||
$this->init();
|
||||
}
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getInternalStatus() : int
|
||||
{
|
||||
return $this->getContent()[self::STATUSCODE];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getStatusMessage() : string
|
||||
{
|
||||
return $this->getContent()[self::STATUSMESSAGE];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function getClientMessageId()
|
||||
{
|
||||
return $this->getContent()[self::CLIENTMESSAGEID];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function getTransferId()
|
||||
{
|
||||
return $this->getContent()[self::TRANSFERID];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function getSmsCount() : int
|
||||
{
|
||||
return $this->getContent()[self::SMSCOUNT];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -35,11 +87,13 @@ abstract class Response implements ResponseInterface
|
||||
*/
|
||||
public function isSuccessful(): bool
|
||||
{
|
||||
return $this->status >= 200 && $this->status < 300;
|
||||
$status = $this->getInternalStatus();
|
||||
|
||||
return $status >= 2000 && $status <= 2999;
|
||||
}
|
||||
|
||||
public function getError(): string
|
||||
public function getErrorMessage(): string
|
||||
{
|
||||
return $this->isSuccessful() ? '' : $this->data['message'];
|
||||
return $this->isSuccessful() ? '' : $this->getStatusMessage();
|
||||
}
|
||||
}
|
||||
|
@ -6,24 +6,21 @@ namespace D3\LinkmobilityClient\Response;
|
||||
|
||||
interface ResponseInterface
|
||||
{
|
||||
/**
|
||||
* Should instantiate the object from the data given
|
||||
*/
|
||||
public function init() : void;
|
||||
public function __construct(\Psr\Http\Message\ResponseInterface $rawResponse);
|
||||
|
||||
public function __construct(array $data);
|
||||
public function getRawResponse() : \Psr\Http\Message\ResponseInterface;
|
||||
|
||||
/**
|
||||
* Must return true if the request was successful
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isSuccessful() : bool;
|
||||
public function getInternalStatus() : int;
|
||||
|
||||
/**
|
||||
* This must return the error, if any occurred, else it must return ''
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getError() : string;
|
||||
public function getStatusMessage() : string;
|
||||
|
||||
public function getClientMessageId();
|
||||
|
||||
public function getTransferId();
|
||||
|
||||
public function getSmsCount() : int;
|
||||
|
||||
public function isSuccessful(): bool;
|
||||
|
||||
public function getErrorMessage(): string;
|
||||
}
|
||||
|
@ -133,7 +133,7 @@ class TextRequest extends \D3\LinkmobilityClient\Request\Request
|
||||
|
||||
public function getUri(): string
|
||||
{
|
||||
return '/smsmessaging/text/';
|
||||
return '/rest/smsmessaging/text';
|
||||
}
|
||||
|
||||
public function validate(): void
|
||||
|
@ -5,21 +5,36 @@ declare(strict_types=1);
|
||||
namespace D3\LinkmobilityClient\ValueObject;
|
||||
|
||||
use Assert\Assert;
|
||||
use libphonenumber\PhoneNumberType;
|
||||
|
||||
class Recipient extends StringValueObject
|
||||
{
|
||||
public function __construct(string $value)
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $countryCode;
|
||||
|
||||
public function __construct(string $number, string $iso2CountryCode)
|
||||
{
|
||||
// ohne +, dafür mit Ländervorwahl
|
||||
// eine führende 0 scheint lokale Version
|
||||
// zwei führende Nullen einfach weggeschnitten
|
||||
Assert::that($iso2CountryCode)->string()->length(2);
|
||||
|
||||
//https://github.com/matmar10/msisdn-format-bundle/blob/master/Matmar10/Bundle/MsisdnFormatBundle/Resources/config/msisdn-country-formats.xml
|
||||
$phoneUtil = \libphonenumber\PhoneNumberUtil::getInstance();
|
||||
try {
|
||||
$phoneNumber = $phoneUtil->parse($number, strtoupper($iso2CountryCode));
|
||||
$number = ltrim($phoneUtil->format($phoneNumber, \libphonenumber\PhoneNumberFormat::E164), '+');
|
||||
} catch (\libphonenumber\NumberParseException $e) {
|
||||
var_dump($e);
|
||||
}
|
||||
|
||||
parent::__construct($number);
|
||||
$this->countryCode = $iso2CountryCode;
|
||||
}
|
||||
|
||||
// valid formats can be found here: https://linkmobility.atlassian.net/wiki/spaces/COOL/pages/26017807/08.+Messages#id-08.Messages-recipients
|
||||
Assert::that($value)->regex('/^(\+|c)?[0-9]+$/i', 'Recipient does not match valid phone number.');
|
||||
|
||||
parent::__construct($value);
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getCountryCode() :string
|
||||
{
|
||||
return $this->countryCode;
|
||||
}
|
||||
}
|
||||
|
@ -8,13 +8,22 @@ use Assert\Assert;
|
||||
|
||||
class Sender extends StringValueObject
|
||||
{
|
||||
public function __construct(string $value)
|
||||
public function __construct(string $number, string $iso2CountryCode)
|
||||
{
|
||||
// if the sender is alphanumeric, test the length
|
||||
if (!preg_match('/^\+[0-9]+$/i', $value)) {
|
||||
Assert::that($value)->maxLength(11);
|
||||
Assert::that($iso2CountryCode)->string()->length(2);
|
||||
|
||||
$phoneUtil = \libphonenumber\PhoneNumberUtil::getInstance();
|
||||
try {
|
||||
$phoneNumber = $phoneUtil->parse( $number, strtoupper($iso2CountryCode) );
|
||||
$number = $phoneUtil->format( $phoneNumber, \libphonenumber\PhoneNumberFormat::E164 );
|
||||
|
||||
if (false === $phoneUtil->isValidNumber($phoneNumber)) {
|
||||
throw new \D3\LinkmobilityClient\Exceptions\RecipientException( 'invalid sender phone number' );
|
||||
}
|
||||
} catch (\libphonenumber\NumberParseException $e) {
|
||||
var_dump($e);
|
||||
}
|
||||
|
||||
parent::__construct($value);
|
||||
parent::__construct( $number);
|
||||
}
|
||||
}
|
||||
|
@ -12,9 +12,9 @@ class SmsMessage extends StringValueObject
|
||||
const GSM_7BIT = '7-bit';
|
||||
const GSM_UCS2 = 'ucs-2';
|
||||
|
||||
public function __construct(string $value)
|
||||
public function __construct(string $number)
|
||||
{
|
||||
parent::__construct($value);
|
||||
parent::__construct( $number);
|
||||
|
||||
$smsLength = new SmsLength($this->value);
|
||||
$smsLength->validate();
|
||||
|
@ -8,11 +8,11 @@ use Assert\Assert;
|
||||
|
||||
abstract class StringValueObject extends ValueObject
|
||||
{
|
||||
public function __construct(string $value)
|
||||
public function __construct(string $number)
|
||||
{
|
||||
Assert::that($value)->notEmpty();
|
||||
Assert::that( $number)->notEmpty();
|
||||
|
||||
$this->value = $value;
|
||||
$this->value = $number;
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
|
Loading…
Reference in New Issue
Block a user