Skip to content

Commit

Permalink
Merge pull request #4 from motveu/update-sdk
Browse files Browse the repository at this point in the history
Update sdk
  • Loading branch information
jakubvojacek authored Nov 11, 2022
2 parents 9238c1d + b71a972 commit 9975155
Show file tree
Hide file tree
Showing 26 changed files with 14,265 additions and 5,912 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,7 @@
.DS_Store
/vendor
/composer.lock
/tests/_support/_generated
/tests/_output/
/tests/config.local.neon
/temp/
12 changes: 12 additions & 0 deletions codeception.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
paths:
tests: tests
output: tests/_output
data: tests/_data
support: tests/_support
envs: tests/_envs
actor_suffix: Tester
extensions:
enabled:
- Codeception\Extension\RunFailed
coverage:
enabled: false
15 changes: 11 additions & 4 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,18 @@
"keywords": ["php", "motveu", "jacon", "sms", "csms", "middleware", "php api"],
"homepage": "https://motv.eu",
"autoload": {
"classmap": ["src/"]
"classmap": ["src/", "tests/"]
},
"require": {
"php": ">=7.2",
"guzzlehttp/guzzle": "^7.0",
"monolog/monolog": "^2.0"
"php": ">=8.1",
"guzzlehttp/guzzle": "^7.0",
"monolog/monolog": "^3.0",
"phpdocumentor/reflection-docblock": "^5.1"
},
"require-dev": {
"codeception/codeception": "^5.0.0",
"codeception/module-phpbrowser": "^2.0",
"codeception/module-asserts": "^3.0",
"nette/neon": "^3.0"
}
}
67 changes: 63 additions & 4 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ For full API documentation and test API endpoint please get in touch with JACON

require_once __DIR__ . '/vendor/autoload.php';

$smsConnector = new \Motv\Sms\Connector('https://sms.operator.tv', 'Username', 'secret...');
$smsConnector = new \Motv\Connector\Sms\AdminConnector('https://sms.operator.tv', 'Username', 'secret...');

// create sample customer
$viewersId = $smsConnector->Integration()->createMotvCustomer('test', 'myPassword');
Expand Down Expand Up @@ -74,10 +74,69 @@ For full API documentation and test API endpoint please get in touch with moTV.e

require_once __DIR__ . '/vendor/autoload.php';

$mwAdminConnector = new \Motv\Mw\AdminConnector('https://mw.operator.tv', 'Username', 'secret');
$mwAdminConnector = new \Motv\Connector\Mw\AdminConnector('https://mw.operator.tv', 'Username', 'secret');

// get customer with internal ID 1
$mwAdminConnector->Customer()->getData(1);
$customerEntity = $mwAdminConnector->Customer()->getData(1);
$vendorsPairs = $mwAdminConnector->Vendor()->getPairs();
echo 'Login: ' . $customerEntity->customers_login;
echo PHP_EOL;
echo 'Vendor: ' . $vendorsPairs[$customerEntity->customers_vendors_id];
```

Works with entities, update, getData and selection
---------------


```php
<?php

require_once __DIR__ . '/vendor/autoload.php';

$mwAdminConnector = new \Motv\Connector\Mw\AdminConnector('https://mw.operator.tv', 'Username', 'secret');

// It creates input entity and fills it with data
$personInputEntity = new \Motv\Connector\Mw\InputEntities\Mw\PersonEntity();
$personInputEntity->persons_type = \Motv\Connector\Mw\Enums\Mw\PersonEnum::ACTOR;
$personInputEntity->persons_birthday = '1990-01-01';
// in case of date, both string and DateTime objects are accept
$personInputEntity->persons_birthday = (new \DateTimeImmutable)->setTimestamp(strtotime('now'));
$personInputEntity->persons_description = 'Popular actor';
$personInputEntity->persons_name = 'John Smith';

// Creates new Person
$personsId = $mwAdminConnector->Person()->update(null, $personInputEntity);

// Gets the new person we just created
$personEntity = $mwAdminConnector->Person()->getData($personsId);
echo 'Actor ' . $personEntity->persons_name . ' with ID: ' . $personEntity->persons_id;
echo PHP_EOL;

// Let's change name of the Person
$personInputEntity->persons_name = 'Will Smith';

// Updates the name
$mwAdminConnector->Person()->update($personsId, $personInputEntity);

// Let's double-check that the changes were actually reflected
$personEntity = $mwAdminConnector->Person()->getData($personsId);
echo 'Actor ' . $personEntity->persons_name . ' with ID: ' . $personEntity->persons_id;
echo PHP_EOL;

// Select the person by selection function
$selectedActorEntity = $mwAdminConnector->Person()->selection(['persons_name' => 'Will Smith'])['rows'][0];
echo 'Actor ' . $selectedActorEntity->persons_name . ' with ID: ' . $selectedActorEntity->persons_id;
echo PHP_EOL;

// sending invalid data will result into a neat error, for example
$personInputEntity = new \Motv\Connector\Mw\InputEntities\Mw\PersonEntity();
$personInputEntity->persons_name = '';
$personInputEntity->persons_type = \Motv\Connector\Mw\Enums\Mw\PersonEnum::ACTOR;
$personInputEntity->persons_birthday = '1990-01-01';
$personInputEntity->persons_description = 'Popular actor';

// Will not create a new person because name is empty, will throw an exception instead
$personsId = $mwAdminConnector->Person()->update(null, $personInputEntity);
```

Catching errors
Expand All @@ -103,7 +162,7 @@ Logging
This SDK is shipped with Monolog (https://github.com/Seldaek/monolog) support. You can pass logger to the `$connector->setLogger()` method and all API communication will be logged

```php
$mwAdminConnector = new \Motv\Mw\AdminConnector('https://mw.operator.tv', 'Username', 'secret');
$mwAdminConnector = new \Motv\Connector\Mw\AdminConnector('https://mw.operator.tv', 'Username', 'secret');

$logger = new \Monolog\Logger('API');
$logger->pushHandler(new \Monolog\Handler\RotatingFileHandler('api.log', 14));
Expand Down
137 changes: 130 additions & 7 deletions src/Connector.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@

declare(strict_types=1);

namespace Motv;
namespace Motv\Connector;

use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\Exception\TransferException;
use Monolog\Logger;
use Motv\Connector\Mw\InputEntities\MotvEntity;
use PHPUnit\Framework\Constraint\ArrayHasKey;

abstract class Connector {

Expand All @@ -23,6 +24,9 @@ abstract class Connector {
/** @var Logger */
protected $logger;

/** @var array */
protected $entitiesNamespaces;

public function __construct(string $url, string $username, string $secret)
{
$this->url = $url;
Expand All @@ -47,7 +51,7 @@ protected function log(string $severity, string $message, array $data = []): voi
$this->logger->$severity($message, $data);
}

public function call(string $model, string $method, array $parameters)
public function call(string $model, string $method, array $parameters, ?string $returnClass = null)
{
$timestamp = time();
$header = $this->username . ':' . $timestamp . ':' . sha1($timestamp . $this->username . $this->secret);
Expand All @@ -56,6 +60,7 @@ public function call(string $model, string $method, array $parameters)

$client = new Client();

$parameters = $this->prepareForRequest($parameters);
$this->log('info', 'Sending request to "' . $finalUrl . '" with data', $parameters);

try {
Expand All @@ -73,22 +78,140 @@ public function call(string $model, string $method, array $parameters)

$responseContent = json_decode($response->getBody()->getContents(), true);

if (!is_array($responseContent) || !array_key_exists('status', $responseContent) || !array_key_exists($responseContent['status'], $this->exceptions)) {
$exceptionName = '\Motv\\' . $this->namespace . '\UnknownApiException';
if ((!is_array($responseContent) || !array_key_exists('status', $responseContent) || !array_key_exists($responseContent['status'], $this->exceptions)) && $responseContent['status'] !== 1) {
$exceptionName = '\Motv\Connector\\' . $this->namespace . '\Exceptions\UnknownApiException';
$this->log('error', 'Received response with unknown error', [$responseContent['response']]);

throw new $exceptionName($responseContent);
} elseif ($responseContent['status'] !== 1) {
$this->log('warning', 'Received response with exception ' . $this->exceptions[$responseContent['status']], [$responseContent['response']]);
$exceptionName = '\Motv\\' . $this->namespace . '\\' . $this->exceptions[$responseContent['status']];
$exceptionName = $this->exceptions[$responseContent['status']];

throw new $exceptionName($responseContent['response']);
}

$this->log('info', 'Received response with data', $responseContent);

return $responseContent['response'];
return is_array($responseContent['response']) ? $this->setUpFromArray($responseContent['response']) : $responseContent['response'];
}

/**
* @param array<string, mixed> $data
* @return mixed
*/
private function setUpFromArray(array $data)
{
if (isset($data['_class'])) {
try {
$className = $data['_class'];
unset($data['_class']);

foreach ($this->entitiesNamespaces as $entitiesNamespace) {
try {
new \ReflectionClass($entitiesNamespace . $className);
$className = $entitiesNamespace . $className;

break;
} catch (\Throwable) {
continue;
}
}

new \ReflectionClass($className);
$instance = new $className();

foreach ($data as $k => $v) {
$property = new \ReflectionProperty($className, $k);
$typeName = $property->getType()->getName();

try {
new \ReflectionEnum($typeName);
$v = ($property->getType()->getName())::from((string) $v);
} catch (\Throwable) {
// ignore
}

try {
$reflectionClass = new \ReflectionClass($typeName);

if ($reflectionClass->getShortName() === 'DateTimeImmutable') {
$date = new \DateTimeImmutable();
$date->setTimestamp(strtotime($v));
$v = $date;
}
} catch (\Throwable) {
// ignore
}

if (is_array($v)) {
$v = $this->setUpFromArray($v);
}

$instance->$k = $v;
}

return $instance;
} catch (\Throwable) {
// ignore
}
} else {
foreach ($data as $k => $v) {
if (is_array($v)) {
$data[$k] = $this->setUpFromArray($v);
}
}

return $data;
}
}

/**
* @param array<string, mixed> $data
* @return array<string, mixed>
*/
private function prepareForRequest(array $data): array
{
foreach ($data as $k => $v) {
if ($v instanceof MotvEntity || $v instanceof \Motv\Connector\Sms\InputEntities\MotvEntity) {
$data[$k] = $this->prepareForRequest($this->serialize($v));
} else if (\is_array($v) || $v instanceof ArrayHasKey) {
$data[$k] = $this->prepareForRequest((array) $v);
} else {
if ($v instanceof \DateTime || $v instanceof \DateTimeImmutable) {
$data[$k] = $v->format('Y-m-d H:i:s');
}
}
}

return $data;
}

private function serialize($entity): array {
$data = [];

foreach (\get_object_vars($entity) as $k => $v) {
$data[$k] = $v instanceof \BackedEnum ? $v->value : $v;
}

return $this->serializeMotvEntity($data);
}

private function serializeMotvEntity($data): array
{
if ($data instanceof MotvEntity || $data instanceof \Motv\Connector\Sms\InputEntities\MotvEntity) {
$data = $this->serialize($data);
}

foreach ($data as $k => $v) {
if (\is_array($v)) {
$data[$k] = self::serializeMotvEntity($v);
} elseif ($v instanceof MotvEntity || $v instanceof \Motv\Connector\Sms\InputEntities\MotvEntity) {
$data[$k] = self::serializeMotvEntity($this->serialize($v));
} elseif ($v instanceof \BackedEnum) {
$data[$k] = $v->value;
}
}

return $data;
}
}
61 changes: 47 additions & 14 deletions src/Mw/AdminConnector.php

Large diffs are not rendered by default.

Loading

0 comments on commit 9975155

Please sign in to comment.