IGNITE-10022: JS, PHP thin clients: a more meaningful exception when
ENUM type is not registered

This closes #5187


Project: http://git-wip-us.apache.org/repos/asf/ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/2ad04930
Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/2ad04930
Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/2ad04930

Branch: refs/heads/ignite-627
Commit: 2ad04930dd5854c4272fb347858292e464daa83d
Parents: 862c926
Author: ekaterina-nbl <ekaterina.vergiz...@nobitlost.com>
Authored: Mon Oct 29 15:49:33 2018 +0300
Committer: Igor Sapego <isap...@apache.org>
Committed: Mon Oct 29 15:49:33 2018 +0300

----------------------------------------------------------------------
 modules/platforms/nodejs/lib/EnumItem.js        |  17 ++-
 modules/platforms/nodejs/lib/Errors.js          |  12 ++
 .../nodejs/lib/internal/BinaryUtils.js          |   4 +
 modules/platforms/nodejs/spec/TestingHelper.js  |   7 ++
 .../spec/cache/CachePutGetDiffTypes.spec.js     |  39 ++++++
 .../Internal/Binary/BinaryCommunicator.php      |  27 +++--
 .../Ignite/Internal/Binary/BinaryUtils.php      |  12 ++
 modules/platforms/php/tests/CachePutGetTest.php | 119 +++++++++++++++++++
 8 files changed, 221 insertions(+), 16 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/2ad04930/modules/platforms/nodejs/lib/EnumItem.js
----------------------------------------------------------------------
diff --git a/modules/platforms/nodejs/lib/EnumItem.js 
b/modules/platforms/nodejs/lib/EnumItem.js
index 1d1725e..5e80da9 100644
--- a/modules/platforms/nodejs/lib/EnumItem.js
+++ b/modules/platforms/nodejs/lib/EnumItem.js
@@ -17,6 +17,7 @@
 
 'use strict';
 
+const Util = require('util');
 const ArgumentChecker = require('./internal/ArgumentChecker');
 const Errors = require('./Errors');
 
@@ -157,14 +158,18 @@ class EnumItem {
      * @ignore
      */
     async _write(communicator, buffer) {
+        const type = await this._getType(communicator, this._typeId);
+        if (!type || !type._isEnum) {
+            throw Errors.IgniteClientError.enumSerializationError(
+                true, Util.format('enum type id "%d" is not registered', 
this._typeId));
+        }
         buffer.writeInteger(this._typeId);
         if (this._ordinal !== null) {
             buffer.writeInteger(this._ordinal);
             return;
         }
         else if (this._name !== null || this._value !== null) {
-            const type = await this._getType(communicator, this._typeId);
-            if (type._isEnum && type._enumValues) {
+            if (type._enumValues) {
                 for (let i = 0; i < type._enumValues.length; i++) {
                     if (this._name === type._enumValues[i][0] ||
                         this._value === type._enumValues[i][1]) {
@@ -185,8 +190,12 @@ class EnumItem {
         this._typeId = buffer.readInteger();
         this._ordinal = buffer.readInteger();
         const type = await this._getType(communicator, this._typeId);
-        if (!type._isEnum || !type._enumValues || type._enumValues.length <= 
this._ordinal) {
-            throw new Errors.IgniteClientError('EnumItem can not be 
deserialized: type mismatch');
+        if (!type || !type._isEnum) {
+            throw Errors.IgniteClientError.enumSerializationError(
+                false, Util.format('enum type id "%d" is not registered', 
this._typeId));
+        }
+        else if (!type._enumValues || type._enumValues.length <= 
this._ordinal) {
+            throw Errors.IgniteClientError.enumSerializationError(false, 'type 
mismatch');
         }
         this._name = type._enumValues[this._ordinal][0];
         this._value = type._enumValues[this._ordinal][1];

http://git-wip-us.apache.org/repos/asf/ignite/blob/2ad04930/modules/platforms/nodejs/lib/Errors.js
----------------------------------------------------------------------
diff --git a/modules/platforms/nodejs/lib/Errors.js 
b/modules/platforms/nodejs/lib/Errors.js
index 57a7a8c..89baf38 100644
--- a/modules/platforms/nodejs/lib/Errors.js
+++ b/modules/platforms/nodejs/lib/Errors.js
@@ -83,6 +83,18 @@ class IgniteClientError extends Error {
         }
         return new IgniteClientError(msg);
     }
+
+    /**
+     * EnumItem serialization/deserialization errors.
+     * @ignore
+     */
+    static enumSerializationError(serialize, message = null) {
+        let msg = serialize ? 'Enum item can not be serialized' : 'Enum item 
can not be deserialized';
+        if (message) {
+            msg = msg + ': ' + message;
+        }
+        return new IgniteClientError(msg);
+    }
 }
 
 /**

http://git-wip-us.apache.org/repos/asf/ignite/blob/2ad04930/modules/platforms/nodejs/lib/internal/BinaryUtils.js
----------------------------------------------------------------------
diff --git a/modules/platforms/nodejs/lib/internal/BinaryUtils.js 
b/modules/platforms/nodejs/lib/internal/BinaryUtils.js
index 2619df7..fe1e403 100644
--- a/modules/platforms/nodejs/lib/internal/BinaryUtils.js
+++ b/modules/platforms/nodejs/lib/internal/BinaryUtils.js
@@ -497,6 +497,10 @@ class BinaryUtils {
             expectedTypeCode === BinaryUtils.TYPE_CODE.COMPLEX_OBJECT) {
             return;
         }
+        else if (expectedTypeCode === BinaryUtils.TYPE_CODE.ENUM &&
+            actualTypeCode === BinaryUtils.TYPE_CODE.BINARY_ENUM) {
+            return;
+        }
         else if (actualTypeCode !== expectedTypeCode) {
             throw Errors.IgniteClientError.typeCastError(actualTypeCode, 
expectedTypeCode);
         }

http://git-wip-us.apache.org/repos/asf/ignite/blob/2ad04930/modules/platforms/nodejs/spec/TestingHelper.js
----------------------------------------------------------------------
diff --git a/modules/platforms/nodejs/spec/TestingHelper.js 
b/modules/platforms/nodejs/spec/TestingHelper.js
index 79a5336..78df0cb 100644
--- a/modules/platforms/nodejs/spec/TestingHelper.js
+++ b/modules/platforms/nodejs/spec/TestingHelper.js
@@ -232,6 +232,13 @@ class TestingHelper {
         TestingHelper.checkError(error, Errors.IgniteClientError, done)
     }
 
+    static checkEnumItemSerializationError(error, done) {
+        if (!(error instanceof Errors.IgniteClientError) ||
+            error.message.indexOf('Enum item can not be serialized') < 0) {
+            done.fail('unexpected error: ' + error);
+        }
+    }
+
     static checkError(error, errorType, done) {
         if (!(error instanceof errorType)) {
             done.fail('unexpected error: ' + error);

http://git-wip-us.apache.org/repos/asf/ignite/blob/2ad04930/modules/platforms/nodejs/spec/cache/CachePutGetDiffTypes.spec.js
----------------------------------------------------------------------
diff --git a/modules/platforms/nodejs/spec/cache/CachePutGetDiffTypes.spec.js 
b/modules/platforms/nodejs/spec/cache/CachePutGetDiffTypes.spec.js
index 28a9ae3..a6e1bba 100644
--- a/modules/platforms/nodejs/spec/cache/CachePutGetDiffTypes.spec.js
+++ b/modules/platforms/nodejs/spec/cache/CachePutGetDiffTypes.spec.js
@@ -543,6 +543,45 @@ describe('cache put get test suite >', () => {
             catch(error => done.fail(error));
     });
 
+    it('put enum items', (done) => {
+        Promise.resolve().
+            then(async () => {
+                const fakeTypeId = 12345;
+                const enumItem1 = new EnumItem(fakeTypeId);
+                enumItem1.setOrdinal(1);
+                await putEnumItem(enumItem1, null, done);
+                await putEnumItem(enumItem1, ObjectType.PRIMITIVE_TYPE.ENUM, 
done);
+                const enumItem2 = new EnumItem(fakeTypeId);
+                enumItem2.setName('name');
+                await putEnumItem(enumItem2, null, done);
+                await putEnumItem(enumItem2, ObjectType.PRIMITIVE_TYPE.ENUM, 
done);
+                const enumItem3 = new EnumItem(fakeTypeId);
+                enumItem3.setValue(2);
+                await putEnumItem(enumItem3, null, done);
+                await putEnumItem(enumItem3, ObjectType.PRIMITIVE_TYPE.ENUM, 
done);
+            }).
+            then(done).
+            catch(error => done.fail(error));
+    });
+
+    async function putEnumItem(value, valueType, done) {
+        const cache = igniteClient.getCache(CACHE_NAME).
+            setKeyType(null).
+            setValueType(valueType);
+        const key = new Date();
+        // Enums registration is not supported by the client, therefore put 
EnumItem must throw IgniteClientError
+        try {
+            await cache.put(key, value);
+            done.fail('put EnumItem must throw IgniteClientError');
+        }
+        catch (err) {
+            TestingHelper.checkEnumItemSerializationError(err, done);
+        }
+        finally {
+            await cache.removeAll();
+        }
+    }
+
     async function putGetPrimitiveValues(keyType, valueType, key, value, 
modificator) {
         const cache = await igniteClient.getCache(CACHE_NAME).
             setKeyType(keyType).

http://git-wip-us.apache.org/repos/asf/ignite/blob/2ad04930/modules/platforms/php/src/Apache/Ignite/Internal/Binary/BinaryCommunicator.php
----------------------------------------------------------------------
diff --git 
a/modules/platforms/php/src/Apache/Ignite/Internal/Binary/BinaryCommunicator.php
 
b/modules/platforms/php/src/Apache/Ignite/Internal/Binary/BinaryCommunicator.php
index 520063c..781a730 100644
--- 
a/modules/platforms/php/src/Apache/Ignite/Internal/Binary/BinaryCommunicator.php
+++ 
b/modules/platforms/php/src/Apache/Ignite/Internal/Binary/BinaryCommunicator.php
@@ -265,8 +265,10 @@ class BinaryCommunicator
         $ordinal = $buffer->readInteger();
         $enumItem->setOrdinal($ordinal);
         $type = $this->typeStorage->getType($enumItem->getTypeId());
-        if (!$type->isEnum() || !$type->getEnumValues() || 
count($type->getEnumValues()) <= $ordinal) {
-            BinaryUtils::serializationError(false, 'EnumItem can not be 
deserialized: type mismatch');
+        if (!$type || !$type->isEnum()) {
+            BinaryUtils::enumSerializationError(false, sprintf('enum type id 
"%d" is not registered', $enumItem->getTypeId()));
+        } elseif (!$type->getEnumValues() || count($type->getEnumValues()) <= 
$ordinal) {
+            BinaryUtils::enumSerializationError(false, 'type mismatch');
         }
         $enumValues = $type->getEnumValues();
         $enumItem->setName($enumValues[$ordinal][0]);
@@ -396,21 +398,22 @@ class BinaryCommunicator
 
     private function writeEnum(MessageBuffer $buffer, EnumItem $enumValue): 
void
     {
+        $type = $this->typeStorage->getType($enumValue->getTypeId());
+        if (!$type || !$type->isEnum()) {
+            BinaryUtils::enumSerializationError(true, sprintf('enum type id 
"%d" is not registered', $enumValue->getTypeId()));
+        }
         $buffer->writeInteger($enumValue->getTypeId());
         if ($enumValue->getOrdinal() !== null) {
             $buffer->writeInteger($enumValue->getOrdinal());
             return;
         } elseif ($enumValue->getName() !== null || $enumValue->getValue() !== 
null) {
-            $type = $this->typeStorage->getType($enumValue->getTypeId());
-            if ($type && $type->isEnum()) {
-                $enumValues = $type->getEnumValues();
-                if ($enumValues) {
-                    for ($i = 0; $i < count($enumValues); $i++) {
-                        if ($enumValue->getName() === $enumValues[$i][0] ||
-                            $enumValue->getValue() === $enumValues[$i][1]) {
-                            $buffer->writeInteger($i);
-                            return;
-                        }
+            $enumValues = $type->getEnumValues();
+            if ($enumValues) {
+                for ($i = 0; $i < count($enumValues); $i++) {
+                    if ($enumValue->getName() === $enumValues[$i][0] ||
+                        $enumValue->getValue() === $enumValues[$i][1]) {
+                        $buffer->writeInteger($i);
+                        return;
                     }
                 }
             }

http://git-wip-us.apache.org/repos/asf/ignite/blob/2ad04930/modules/platforms/php/src/Apache/Ignite/Internal/Binary/BinaryUtils.php
----------------------------------------------------------------------
diff --git 
a/modules/platforms/php/src/Apache/Ignite/Internal/Binary/BinaryUtils.php 
b/modules/platforms/php/src/Apache/Ignite/Internal/Binary/BinaryUtils.php
index ad0bf56..e9ff2f1 100644
--- a/modules/platforms/php/src/Apache/Ignite/Internal/Binary/BinaryUtils.php
+++ b/modules/platforms/php/src/Apache/Ignite/Internal/Binary/BinaryUtils.php
@@ -203,6 +203,9 @@ class BinaryUtils
             $actualTypeCode === ObjectType::BINARY_OBJECT &&
             $expectedTypeCode === ObjectType::COMPLEX_OBJECT) {
             return;
+        } elseif ($expectedTypeCode === ObjectType::ENUM &&
+            $actualTypeCode === ObjectType::BINARY_ENUM) {
+            return;
         } elseif ($actualTypeCode !== $expectedTypeCode) {
             BinaryUtils::typeCastError($actualTypeCode, $expectedTypeCode);
         }
@@ -419,6 +422,15 @@ class BinaryUtils
         throw new ClientException($msg);
     }
 
+    public static function enumSerializationError(bool $serialize, string 
$message = null): void
+    {
+        $msg = $serialize ? 'Enum item can not be serialized' : 'Enum item can 
not be deserialized';
+        if ($message) {
+            $msg = $msg . ': ' . $message;
+        }
+        throw new ClientException($msg);
+    }
+
     public static function typeCastError($fromType, $toType): void
     {
         throw new ClientException(sprintf('Type "%s" can not be cast to %s',

http://git-wip-us.apache.org/repos/asf/ignite/blob/2ad04930/modules/platforms/php/tests/CachePutGetTest.php
----------------------------------------------------------------------
diff --git a/modules/platforms/php/tests/CachePutGetTest.php 
b/modules/platforms/php/tests/CachePutGetTest.php
index 9d15ab2..c3ff1e9 100644
--- a/modules/platforms/php/tests/CachePutGetTest.php
+++ b/modules/platforms/php/tests/CachePutGetTest.php
@@ -18,14 +18,20 @@
 
 namespace Apache\Ignite\Tests;
 
+use \DateTime;
 use Ds\Map;
 use Ds\Set;
 use PHPUnit\Framework\TestCase;
+use Apache\Ignite\Type\ObjectType;
 use Apache\Ignite\Type\MapObjectType;
 use Apache\Ignite\Type\CollectionObjectType;
 use Apache\Ignite\Type\ObjectArrayType;
 use Apache\Ignite\Type\ComplexObjectType;
 use Apache\Ignite\Data\BinaryObject;
+use Apache\Ignite\Data\Date;
+use Apache\Ignite\Data\Timestamp;
+use Apache\Ignite\Data\EnumItem;
+use Apache\Ignite\Exception\ClientException;
 
 class TstComplObjectWithPrimitiveFields
 {
@@ -490,6 +496,119 @@ final class CachePutGetTestCase extends TestCase
         $this->putGetObjectArrays(new ObjectArrayType(new ObjectArrayType(new 
ComplexObjectType())), $array);
     }
 
+    public function testPutGetDateTime(): void
+    {
+        $this->putGetDate("Y-m-d H:i:s", "2018-10-19 18:31:13", 0);
+        $this->putGetDate("Y-m-d H:i:s", "2018-10-19 18:31:13", 29726);
+        $this->putGetDate("Y-m-d H:i:s", "2018-10-19 18:31:13", 999999);
+
+        $this->putGetTimestamp("Y-m-d H:i:s", "2018-10-19 18:31:13", 0);
+        $this->putGetTimestamp("Y-m-d H:i:s", "2018-10-19 18:31:13", 29726000);
+        $this->putGetTimestamp("Y-m-d H:i:s", "2018-10-19 18:31:13", 
999999999);
+
+        $this->putGetTimestampFromDateTime("Y-m-d H:i:s", "2018-10-19 
18:31:13", 0);
+        $this->putGetTimestampFromDateTime("Y-m-d H:i:s", "2018-10-19 
18:31:13", 29726);
+        $this->putGetTimestampFromDateTime("Y-m-d H:i:s", "2018-10-19 
18:31:13", 999999);
+    }
+
+    public function testPutEnumItems(): void
+    {
+        $fakeTypeId = 12345;
+        $enumItem1 = new EnumItem($fakeTypeId);
+        $enumItem1->setOrdinal(1);
+        $this->putEnumItem($enumItem1, null);
+        $this->putEnumItem($enumItem1, ObjectType::ENUM);
+        $enumItem2 = new EnumItem($fakeTypeId);
+        $enumItem2->setName('name');
+        $this->putEnumItem($enumItem2, null);
+        $this->putEnumItem($enumItem2, ObjectType::ENUM);
+        $enumItem3 = new EnumItem($fakeTypeId);
+        $enumItem3->setOrdinal(2);
+        $this->putEnumItem($enumItem3, null);
+        $this->putEnumItem($enumItem3, ObjectType::ENUM);
+    }
+
+    private function putEnumItem($value, $valueType): void
+    {
+        $key = microtime();
+        self::$cache->
+            setKeyType(null)->
+            setValueType($valueType);
+        // Enums registration is not supported by the client, therefore put 
EnumItem must throw ClientException
+        try {
+            self::$cache->put($key, $value);
+            $this->fail('put EnumItem must throw ClientException');
+        } catch (ClientException $e) {
+            $this->assertContains('Enum item can not be serialized', 
$e->getMessage());
+        } finally {
+            self::$cache->removeAll();
+        }
+    }
+
+    private function putGetDate(string $format, string $dateString, int 
$micros): void
+    {
+        $key = microtime();
+        self::$cache->
+            setKeyType(null)->
+            setValueType(ObjectType::DATE);
+        try {
+            $dt = DateTime::createFromFormat("$format.u", sprintf("%s.%06d", 
$dateString, $micros));
+            $iDate = Date::fromDateTime($dt);
+            self::$cache->put($key, $iDate);
+            $result = self::$cache->get($key);
+
+            $this->assertEquals(sprintf("%06d", intval($micros / 1000) * 
1000), $result->toDateTime()->format('u'));
+            $this->assertEquals($dateString, 
$result->toDateTime()->format($format));
+        } finally {
+            self::$cache->removeAll();
+        }
+    }
+
+    private function putGetTimestamp(string $format, string $dateString, int 
$nanos): void
+    {
+        $key = microtime();
+        self::$cache->
+            setKeyType(null)->
+            setValueType(ObjectType::TIMESTAMP);
+
+        try {
+            $millis = intval($nanos / 1000000);
+            $nanosInMillis = $nanos % 1000000;
+            self::$cache->put($key,
+                new Timestamp(
+                    DateTime::createFromFormat($format, 
$dateString)->getTimestamp() * 1000 + $millis,
+                    $nanosInMillis
+                )
+            );
+            $result = self::$cache->get($key);
+
+            $this->assertEquals($nanos % 1000000, $result->getNanos());
+            $this->assertEquals($dateString, 
$result->toDateTime()->format($format));
+        } finally {
+            self::$cache->removeAll();
+        }
+    }
+
+    private function putGetTimestampFromDateTime(string $format, string 
$dateString, $micros): void
+    {
+        $key = microtime();
+        self::$cache->
+            setKeyType(null)->
+            setValueType(ObjectType::TIMESTAMP);
+
+        try {
+            self::$cache->put($key, Timestamp::fromDateTime(
+                DateTime::createFromFormat("$format.u", sprintf("%s.%06d", 
$dateString, $micros))
+            ));
+            $result = self::$cache->get($key);
+
+            $this->assertEquals(intval($micros / 1000) * 1000, 
$result->toDateTime()->format('u'));
+            $this->assertEquals($dateString, 
$result->toDateTime()->format($format));
+        } finally {
+            self::$cache->removeAll();
+        }
+    }
+
     private function putGetObjectArrays(?ObjectArrayType $arrayType, array 
$value): void
     {
         $key = microtime();

Reply via email to