Merge pull request #17 from nopolabs/develop

add FileExporter for PHP SDK
diff --git a/composer.json b/composer.json
index a7ec9b5..3725bbb 100644
--- a/composer.json
+++ b/composer.json
@@ -25,7 +25,8 @@
         "guzzlehttp/guzzle": "~5.0"
     },
     "require-dev": {
-        "symfony/class-loader": "~2.3"
+        "symfony/class-loader": "~2.3",
+        "phpunit/phpunit": "4.6.*"
     },
     "autoload": {
         "psr-4": {
diff --git a/src/predictionio/Exporter.php b/src/predictionio/Exporter.php
new file mode 100644
index 0000000..35b7a03
--- /dev/null
+++ b/src/predictionio/Exporter.php
@@ -0,0 +1,62 @@
+<?php
+
+namespace predictionio;
+
+
+trait Exporter {
+
+    abstract public function export($json);
+
+    public function jsonEncode($data) {
+        return json_encode($data);
+    }
+
+    /**
+     * Create and export a json-encoded event.
+     *
+     * @see \predictionio\EventClient::CreateEvent()
+     *
+     * @param $event
+     * @param $entityType
+     * @param $entityId
+     * @param null $targetEntityType
+     * @param null $targetEntityId
+     * @param array $properties
+     * @param $eventTime
+     */
+    public function createEvent($event, $entityType, $entityId,
+                                $targetEntityType=null, $targetEntityId=null, array $properties=null,
+                                $eventTime=null) {
+
+        if (!isset($eventTime)) {
+            $eventTime = new \DateTime();
+        } elseif (!($eventTime instanceof \DateTime)) {
+            $eventTime = new \DateTime($eventTime);
+        }
+        $eventTime = $eventTime->format(\DateTime::ISO8601);
+
+        $data = [
+            'event' => $event,
+            'entityType' => $entityType,
+            'entityId' => $entityId,
+            'eventTime' => $eventTime,
+        ];
+
+        if (isset($targetEntityType)) {
+            $data['targetEntityType'] = $targetEntityType;
+        }
+
+        if (isset($targetEntityId)) {
+            $data['targetEntityId'] = $targetEntityId;
+        }
+
+        if (isset($properties)) {
+            $data['properties'] = $properties;
+        }
+
+        $json = $this->jsonEncode($data);
+
+        $this->export($json);
+    }
+
+}
\ No newline at end of file
diff --git a/src/predictionio/FileExporter.php b/src/predictionio/FileExporter.php
new file mode 100644
index 0000000..d0ee711
--- /dev/null
+++ b/src/predictionio/FileExporter.php
@@ -0,0 +1,27 @@
+<?php
+
+namespace predictionio;
+
+/**
+ * Class FileExporter writes events to a series of JSON objects in a file for batch import.
+ *
+ * @package predictionio
+ */
+class FileExporter {
+
+    use Exporter;
+
+    private $file;
+
+    public function __construct($fileName) {
+        $this->file = fopen($fileName, 'w');
+    }
+
+    public function export($json) {
+        fwrite($this->file, "$json\n");
+    }
+
+    public function close() {
+        fclose($this->file);
+    }
+}
\ No newline at end of file
diff --git a/tests/Unit/ExporterTest.php b/tests/Unit/ExporterTest.php
new file mode 100644
index 0000000..fb2377b
--- /dev/null
+++ b/tests/Unit/ExporterTest.php
@@ -0,0 +1,120 @@
+<?php
+
+namespace predictionio\tests\Unit;
+
+
+use predictionio\Exporter;
+
+class TestExporter {
+    use Exporter {
+        jsonEncode as traitJsonEncode;
+    }
+
+    public $json;
+    public $data;
+
+    public function __construct() {
+        $this->json = [];
+        $this->data = [];
+    }
+
+    public function jsonEncode($data) {
+        $this->data[] = $data;
+        return $this->traitJsonEncode($data);
+    }
+
+    public function export($json) {
+        $this->json[] = $json;
+    }
+}
+
+class ExporterTest extends \PHPUnit_Framework_TestCase {
+
+    /** @var TestExporter $exporter */
+    private $exporter;
+
+    protected function setUp() {
+        $this->exporter = new TestExporter();
+    }
+
+    public function testTimeIsNow() {
+        $time = new \DateTime();
+
+        $this->exporter->createEvent('event', 'entity-type', 'entity-id');
+
+        $this->assertEquals(1, count($this->exporter->data));
+        $data = $this->exporter->data[0];
+        $this->assertEquals(4, count($data));
+        $this->assertEquals('event', $data['event']);
+        $this->assertEquals('entity-type', $data['entityType']);
+        $this->assertEquals('entity-id', $data['entityId']);
+        $this->assertEquals($time->format(\DateTime::ISO8601), $data['eventTime'], 'time is now', 1);
+
+        $this->assertEquals(1, count($this->exporter->json));
+        $json = $this->exporter->json[0];
+        $pattern = '/^{"event":"event","entityType":"entity-type","entityId":"entity-id","eventTime":"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}[+-]\d{4}"}$/';
+        $this->assertTrue(preg_match($pattern, $json) === 1, 'json');
+    }
+
+    public function testTimeIsString() {
+        $time = new \DateTime('2015-04-01');
+
+        $this->exporter->createEvent('event', 'entity-type', 'entity-id', null, null, null, '2015-04-01');
+
+        $this->assertEquals(1, count($this->exporter->data));
+        $data = $this->exporter->data[0];
+        $this->assertEquals(4, count($data));
+        $this->assertEquals('event', $data['event']);
+        $this->assertEquals('entity-type', $data['entityType']);
+        $this->assertEquals('entity-id', $data['entityId']);
+        $this->assertEquals($time->format(\DateTime::ISO8601), $data['eventTime'], 'time is string', 1);
+
+        $this->assertEquals(1, count($this->exporter->json));
+        $json = $this->exporter->json[0];
+        $pattern = '/^{"event":"event","entityType":"entity-type","entityId":"entity-id","eventTime":"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}[+-]\d{4}"}$/';
+        $this->assertTrue(preg_match($pattern, $json) === 1, 'json');
+    }
+
+    public function testTimeIsDateTime() {
+        $time = new \DateTime('2015-04-01');
+
+        $this->exporter->createEvent('event', 'entity-type', 'entity-id', null, null, null, $time);
+
+        $this->assertEquals(1, count($this->exporter->data));
+        $data = $this->exporter->data[0];
+        $this->assertEquals(4, count($data));
+        $this->assertEquals('event', $data['event']);
+        $this->assertEquals('entity-type', $data['entityType']);
+        $this->assertEquals('entity-id', $data['entityId']);
+        $this->assertEquals($time->format(\DateTime::ISO8601), $data['eventTime'], 'time is DateTime', 1);
+
+        $this->assertEquals(1, count($this->exporter->json));
+        $json = $this->exporter->json[0];
+        $pattern = '/^{"event":"event","entityType":"entity-type","entityId":"entity-id","eventTime":"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}[+-]\d{4}"}$/';
+        $this->assertTrue(preg_match($pattern, $json) === 1, 'json');
+    }
+
+    public function testOptionalFields() {
+        $time = new \DateTime('2015-04-01');
+
+        $this->exporter->createEvent('event', 'entity-type', 'entity-id',
+            'target-entity-type', 'target-entity-id', ['property' => true], $time);
+
+        $this->assertEquals(1, count($this->exporter->data));
+        $data = $this->exporter->data[0];
+        $this->assertEquals(7, count($data));
+        $this->assertEquals('event', $data['event']);
+        $this->assertEquals('entity-type', $data['entityType']);
+        $this->assertEquals('entity-id', $data['entityId']);
+        $this->assertEquals($time->format(\DateTime::ISO8601), $data['eventTime'], 'time is DateTime', 1);
+        $this->assertEquals('target-entity-type', $data['targetEntityType']);
+        $this->assertEquals('target-entity-id', $data['targetEntityId']);
+        $this->assertEquals(['property'=>true], $data['properties']);
+
+        $this->assertEquals(1, count($this->exporter->json));
+        $json = $this->exporter->json[0];
+        $pattern = '/^{"event":"event","entityType":"entity-type","entityId":"entity-id","eventTime":"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}[+-]\d{4}","targetEntityType":"target-entity-type","targetEntityId":"target-entity-id","properties":{"property":true}}$/';
+        $this->assertTrue(preg_match($pattern, $json) === 1, 'json');
+    }
+
+}
\ No newline at end of file
diff --git a/tests/Unit/FileExporterTest.php b/tests/Unit/FileExporterTest.php
new file mode 100644
index 0000000..f23affa
--- /dev/null
+++ b/tests/Unit/FileExporterTest.php
@@ -0,0 +1,40 @@
+<?php
+
+namespace predictionio\tests\Unit;
+
+
+use predictionio\FileExporter;
+
+class FileExporterTest extends \PHPUnit_Framework_TestCase {
+
+    public function setUp()
+    {
+        register_shutdown_function(function () {
+            if (file_exists('temp.file')) {
+                unlink('temp.file');
+            }
+        });
+    }
+
+    public function testExporter() {
+        $exporter = new FileExporter('temp.file');
+        $exporter->createEvent('event-1', 'entity-type-1', 'entity-id-1',
+            null, null, null, '2015-04-01');
+        $exporter->createEvent('event-2', 'entity-type-2', 'entity-id-2',
+            'target-entity-type-2', 'target-entity-id-2', ['property' => 'blue'], '2015-04-01');
+        $exporter->close();
+
+        $exported = file_get_contents('temp.file');
+
+        $date = new \DateTime('2015-04-01');
+        $expectedDate = $date->format(\DateTime::ISO8601);
+
+        $expected =<<<EOS
+{"event":"event-1","entityType":"entity-type-1","entityId":"entity-id-1","eventTime":"$expectedDate"}
+{"event":"event-2","entityType":"entity-type-2","entityId":"entity-id-2","eventTime":"$expectedDate","targetEntityType":"target-entity-type-2","targetEntityId":"target-entity-id-2","properties":{"property":"blue"}}
+
+EOS;
+
+        $this->assertEquals($expected, $exported);
+    }
+}
\ No newline at end of file