Skip to content

Commit

Permalink
Merge pull request #10 from stefanak-michal/issue/9_fault_tolerant
Browse files Browse the repository at this point in the history
added error codes constants. added fault tolerant marker.
  • Loading branch information
stefanak-michal authored Aug 4, 2021
2 parents 5ad34c6 + bfad9a6 commit 4aa206e
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 22 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

PHP API library following Deepr specification.

![](https://img.shields.io/badge/phpunit-passed-success) ![](https://img.shields.io/badge/coverage-98%25-green) ![](https://img.shields.io/github/stars/stefanak-michal/deepr-php) ![](https://img.shields.io/packagist/dt/stefanak-michal/deepr-php) ![](https://img.shields.io/github/v/release/stefanak-michal/deepr-php) ![](https://img.shields.io/github/commits-since/stefanak-michal/deepr-php/latest)
![](https://img.shields.io/badge/phpunit-passed-success) ![](https://img.shields.io/badge/coverage-96%25-green) ![](https://img.shields.io/github/stars/stefanak-michal/deepr-php) ![](https://img.shields.io/packagist/dt/stefanak-michal/deepr-php) ![](https://img.shields.io/github/v/release/stefanak-michal/deepr-php) ![](https://img.shields.io/github/commits-since/stefanak-michal/deepr-php/latest)

## Usage

Expand Down
63 changes: 47 additions & 16 deletions src/Deepr.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,27 @@ final class Deepr
*/
const OPTION_AUTHORIZER = 6;

/**
* Authorizer
*/
const ERROR_AUTHORIZER = 1;
/**
* Set options
*/
const ERROR_OPTIONS = 2;
/**
* Get class instance
*/
const ERROR_INSTANCE = 3;
/**
* Wrong structure of classes
*/
const ERROR_STRUCTURE = 4;
/**
* Missing property or method
*/
const ERROR_MISSING = 5;

/**
* Enable to see query traversal
* @var bool
Expand Down Expand Up @@ -119,7 +140,7 @@ public function setOptions(array $options = []): Deepr
if (empty($this->options[$constant->getValue()]))
$this->options[$constant->getValue()] = '';
else
throw new Exception($constant->getName() . ' accept value of type string', 2);
throw new Exception($constant->getName() . ' accept value of type string', self::ERROR_OPTIONS);
}
break;
case 'mixed':
Expand All @@ -129,15 +150,15 @@ public function setOptions(array $options = []): Deepr
if (empty($this->options[$constant->getValue()]))
$this->options[$constant->getValue()] = [];
else
throw new Exception($constant->getName() . ' accept value of type array', 2);
throw new Exception($constant->getName() . ' accept value of type array', self::ERROR_OPTIONS);
}
break;
case 'callable':
if (!is_callable($this->options[$constant->getValue()])) {
if (empty($this->options[$constant->getValue()]))
$this->options[$constant->getValue()] = null;
else
throw new Exception($constant->getName() . ' accept value of type callable or null', 2);
throw new Exception($constant->getName() . ' accept value of type callable or null', self::ERROR_OPTIONS);
}
break;
}
Expand All @@ -157,11 +178,11 @@ public function setOptions(array $options = []): Deepr
private function getInstance(array $args): IComponent
{
if (!array_key_exists($this->options[self::OPTION_SV_KEY], $args))
throw new Exception('Source values type key not found in arguments', 3);
throw new Exception('Source values type key not found in arguments', self::ERROR_INSTANCE);

$cls = $this->options[self::OPTION_SV_NS] . $args[$this->options[self::OPTION_SV_KEY]];
if (!class_exists($cls))
throw new Exception('Requested class "' . $cls . '" does not exists', 3);
throw new Exception('Requested class "' . $cls . '" does not exists', self::ERROR_INSTANCE);

$reflection = new ReflectionClass($cls);
$invokeArgs = [];
Expand All @@ -174,7 +195,7 @@ private function getInstance(array $args): IComponent

$instance = new $cls(...$invokeArgs);
if (!($instance instanceof IComponent))
throw new Exception($cls . ' has to implement IComponent', 3);
throw new Exception($cls . ' has to implement IComponent', self::ERROR_STRUCTURE);

return $instance;
}
Expand Down Expand Up @@ -205,7 +226,7 @@ private function recursion(IComponent &$root, string $action, array $values)
} elseif ($k === '[]' && !empty($action)) {
$this->debug($action . ' []');
if (!($root instanceof ILoadable))
throw new Exception('To access collection of class it has to implement ILoadable interface', 4);
throw new Exception('To access collection of class it has to implement ILoadable interface', self::ERROR_STRUCTURE);

$tmpValues = $values;
unset($tmpValues['[]']);
Expand All @@ -221,6 +242,7 @@ private function recursion(IComponent &$root, string $action, array $values)
$root->add($child, $name);
}
}
return;
} elseif ($k === '()') {
continue;
} elseif (is_array($v) && array_key_exists('()', $v)) {
Expand All @@ -235,30 +257,38 @@ private function recursion(IComponent &$root, string $action, array $values)
if ($root === $data) {
$root = new Collection();
}
$this->recursion($data, $key, $v);
if ($data instanceof Collection) {
if ($data instanceof Collection && count($data->getChildren())) {
foreach ($data->getChildren() as $child) {
$this->recursion($child, '', $v);
}
} else {
$this->recursion($data, $key, $v);
}
$root->add($data, $k);
} elseif ($root instanceof Collection) {
} elseif ($root instanceof Collection && count($root->getChildren()) && method_exists($root->getChildren()[0], $key)) {
foreach ($root->getChildren() as $child) {
$this->recursion($child, $k, $v);
}
} elseif (substr($k, -1) !== '?') {
throw new Exception('Missing method ' . $key, self::ERROR_MISSING);
}
} elseif ($v === true) {
$this->debug($action . ' ' . $k . ' true');
if (property_exists($root, $key) && $this->checkPropertyKey($key)) {
$this->authorize($key);
$root->add(new Value($root->$key), $k);
if (property_exists($root, $key)) {
if ($this->checkPropertyKey($key)) {
$this->authorize($key);
$root->add(new Value($root->$key), $k);
}
} elseif (substr($k, -1) !== '?') {
throw new Exception('Missing property ' . $key, self::ERROR_MISSING);
}
} elseif (property_exists($root, $key)) {
$this->debug('property ' . $key);
$collection = $root->$key;
if (is_string($collection) && class_exists($collection)) {
if (is_string($collection) && class_exists($collection))
$collection = new $collection();
}
if (!($collection instanceof Collection))
throw new Exception('Property has to be instance of collection class or class name', self::ERROR_STRUCTURE);
$this->recursion($collection, $key, $v);
$root->add($collection, $k);
} elseif (is_array($v)) {
Expand Down Expand Up @@ -298,7 +328,7 @@ private function checkPropertyKey(string $key): bool
private function authorize(string $key, string $operation = 'get')
{
if (is_callable($this->options[self::OPTION_AUTHORIZER]) && $this->options[self::OPTION_AUTHORIZER]($key, $operation) === false) {
throw new Exception('Operation not allowed by authorizer', 1);
throw new Exception('Operation not allowed by authorizer', self::ERROR_AUTHORIZER);
}
}

Expand All @@ -310,6 +340,7 @@ private function authorize(string $key, string $operation = 'get')
*/
private function getKey(string $key, bool $alias = true): string
{
$key = rtrim($key, '?');
if (strpos($key, '=>') === false)
return $key;

Expand Down
29 changes: 24 additions & 5 deletions tests/DeeprTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ public function testDeepr(): ?Deepr
*/
public function testInvokeQueries(string $input, string $output, Deepr $deepr)
{
echo json_encode(json_decode($input, true), JSON_PRETTY_PRINT)
. PHP_EOL
. json_encode(json_decode($output, true), JSON_PRETTY_PRINT);

try {
$root = new Root();
$input = json_decode($input, true);
Expand Down Expand Up @@ -108,7 +112,7 @@ public function testException(Deepr $deepr)
{
$root = new Root();
$this->expectException(Exception::class);
$this->expectExceptionCode(4);
$this->expectExceptionCode($deepr::ERROR_STRUCTURE);
$deepr->invokeQuery($root, json_decode('{ "[]": [] }', true));
}

Expand Down Expand Up @@ -191,7 +195,7 @@ public function testOptionAuthorizerException(Deepr $deepr)
{
$root = new Root();
$this->expectException(Exception::class);
$this->expectExceptionCode(1);
$this->expectExceptionCode($deepr::ERROR_AUTHORIZER);
$deepr->invokeQuery($root, json_decode('{"sayHello":{"()":["John"]}}', true), [
$deepr::OPTION_AUTHORIZER => function (string $key, string $operation) {
return false;
Expand Down Expand Up @@ -223,7 +227,7 @@ public function testSetOptions(Deepr $deepr)
public function testSetOptionsStringException(Deepr $deepr)
{
$this->expectException(Exception::class);
$this->expectExceptionCode(2);
$this->expectExceptionCode($deepr::ERROR_OPTIONS);
$deepr->setOptions([
$deepr::OPTION_SV_KEY => ['this has to be string and not a array'],
]);
Expand All @@ -236,7 +240,7 @@ public function testSetOptionsStringException(Deepr $deepr)
public function testSetOptionsArrayException(Deepr $deepr)
{
$this->expectException(Exception::class);
$this->expectExceptionCode(2);
$this->expectExceptionCode($deepr::ERROR_OPTIONS);
$deepr->setOptions([
$deepr::OPTION_IGNORE_KEYS => 'has to be array or empty value',
]);
Expand All @@ -249,9 +253,24 @@ public function testSetOptionsArrayException(Deepr $deepr)
public function testSetOptionsCallableException(Deepr $deepr)
{
$this->expectException(Exception::class);
$this->expectExceptionCode(2);
$this->expectExceptionCode($deepr::ERROR_OPTIONS);
$deepr->setOptions([
$deepr::OPTION_AUTHORIZER => 'this has to be callable or null',
]);
}

/**
* @depends testDeepr
* @param Deepr $deepr
*/
public function testFaultTolerant(Deepr $deepr)
{
try {
$root = new Root();
$result = $deepr->invokeQuery($root, json_decode('{"abc?": true, "method?": {"()": []}}', true));
$this->assertEquals([], $result);
} catch (Exception $e) {
$this->markTestIncomplete($e->getMessage());
}
}
}

0 comments on commit 4aa206e

Please sign in to comment.