From f62e49cbff41841a701b429c40452cc8cdfa89e6 Mon Sep 17 00:00:00 2001 From: Keith Kirk Date: Fri, 22 Nov 2013 23:16:18 -0800 Subject: [PATCH] Adds methods to the client for `addEvent` and `addEvents`. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This removes the need for a user to pass their event data nested inside an array with a ‘data’ key. Renaming `data` to `keen_io_event` / `keen_io_events` in the service description to better namespace/avoid collisions down the road. In hindsight, `data` was too common of a property name. Added additional Unit Testing. Minor CS fixes. Documentation update and added basic change log doc. --- CHANGE.md | 31 ++ README.md | 329 +++++++++--------- phpunit.xml.dist | 9 +- src/KeenIO/Client/KeenIOClient.php | 87 +++-- src/KeenIO/Resources/config/keen-io-3_0.json | 4 +- tests/KeenIOClientTest.php | 302 ---------------- tests/Tests/Client/KeenIOClientTest.php | 212 +++++++++++ .../Tests/Client/KeenIOServiceMethodsTest.php | 183 ++++++++++ tests/bootstrap.php | 35 ++ tests/mock/add-event.mock | 14 + tests/mock/add-events.mock | 14 + tests/mock/invalid-auth.mock | 11 + tests/mock/invalid-project-id.mock | 11 + tests/mock/valid-response.mock | 15 + 14 files changed, 765 insertions(+), 492 deletions(-) create mode 100644 CHANGE.md delete mode 100755 tests/KeenIOClientTest.php create mode 100755 tests/Tests/Client/KeenIOClientTest.php create mode 100755 tests/Tests/Client/KeenIOServiceMethodsTest.php create mode 100644 tests/bootstrap.php create mode 100644 tests/mock/add-event.mock create mode 100644 tests/mock/add-events.mock create mode 100644 tests/mock/invalid-auth.mock create mode 100644 tests/mock/invalid-project-id.mock create mode 100644 tests/mock/valid-response.mock diff --git a/CHANGE.md b/CHANGE.md new file mode 100644 index 0000000..aea1c4e --- /dev/null +++ b/CHANGE.md @@ -0,0 +1,31 @@ +Change Log +========== + +Version 2.0 +----------- + +####addEvent method - ( [source](src/KeenIO/Client/KeenIOClient.php#L112) ) + +Previously, this method required nesting your event data under an extra `data` property - which is no longer necessary. + +The `data` property has been renamed in the [service description](src/KeenIO/Resources/config/keen-io-3_0.json#L71) to `keen_io_event`, to effectively namespace this property. + +#####Changed from: + $client->addEvent($collection, array('data' => $event)); + +#####To: + $client->addEvent($collection, $event); + +####addEvents method - ( [source](src/KeenIO/Client/KeenIOClient.php#L132) ) + +Previously, this method required nesting your array of events under an extra `data` property - which is no longer necessary. + +The `data` property has been renamed in the [service description](src/KeenIO/Resources/config/keen-io-3_0.json#L89) to `keen_io_events`, to effectively namespace this property. + +#####Changed from: + $client->addEvents(array('data' => $events)); + +#####To: + $client->addEvents($events); + +--- diff --git a/README.md b/README.md index 14c3bce..98d9dab 100755 --- a/README.md +++ b/README.md @@ -1,163 +1,166 @@ -Keen IO PHP Library -=================== -The Keen IO API lets developers build analytics features directly into their apps. - -[![Build Status](https://travis-ci.org/keenlabs/KeenClient-PHP.png?branch=master)](https://travis-ci.org/keenlabs/KeenClient-PHP) - -Installation with Composer --------------------------- - 1. Install composer via via `curl -s http://getcomposer.org/installer | php` (on windows, download - http://getcomposer.org/installer and execute it with PHP) - 2. Edit your `composer.json` file with following contents: - - ```json - "require": { - "keen-io/keen-io": "~1.1" - } - ``` - 3. Run `php composer.phar install` - -Usage ------ - -This client was built using [Guzzle](http://guzzlephp.org/), a PHP HTTP client & framework for building RESTful web service clients. - -When you first create a new `KeenIOClient` instance you can pass configuration settings like your Project Id and API Keys in an array -to the factory method. These are optional and can be later specified through Setter methods. - -For certain API Resources, the Master API Key is required and can also be passed to the factory method in the configuration array. -Please read the [Security Documentation](https://keen.io/docs/security/) regarding this Master API key. - -For Requests, the `KeenIOClient` will determine what API Key should be passed based on the type of Request and configuration in the -[Service Description](/src/KeenIO/Resources/config/keen-io-3_0.json). The API Key is passed in the `Authorization` header of the request. - -For a list of required and available parameters for the different API Endpoints, please consult the Keen IO -[API Reference Docs](https://keen.io/docs/api/reference/). - - -#### Configuring the Client - -The factory method accepts an array of configuration settings for the Keen IO Webservice Client. - -Setting | Property Name | Description ---- | --- | --- -Project ID | `projectId` | The Keen IO Project Id for your specific project -Master API Key | `masterKey` | The Keen IO Master API Key - the one API key to rule them all -Read API Key | `readKey` | The Read API Key - used for access to read only (GET|HEAD) operations of the API -Write API Key | `writeKey` | The Write API Key - used for write (PUT|POST Requests) operations of the API -API Version | `version` | The API Version. Currently used to version the API URL and Service Description - -When passing `version` to the factory method or using the `setVersion()` method, the Client will try to load a client Service Description -that matches that version. That Service Description defines the operations available to the Webservice Client. - -Currently the Keen IO Webservice Client only supports - and automatically defaults - to the current version (`3.0`) of the API. - -###### Example -```php -use KeenIO\Client\KeenIOClient; - -$client = KeenIOClient::factory([ - 'projectId' => $projectId, - 'writeKey' => $writeKey, - 'readKey' => $readKey -]); -``` - -#### Configuration can be updated to reuse the same Client: -You can reconfigure the Keen IO Client configuration options through available getters and setters. You can get and set the following options: -`projectId`, `readKey`, `writeKey`, `masterKey`, & `version`. - -###### Example -```php - -//Get the current Project Id -$client->getProjectId(); - -//Set a new Project Id -$client->setProjectId($someNewProjectId); - -//Get the current Read Key -$client->getReadKey(); - -//Set a new Read Key -$newReadKey = $client->getScopedKey($masterKey, $filters, $allowedOperations); -$client->setReadKey($newReadKey); - -``` - -####Send an event to Keen -Once you've created a `KeenIOClient`, sending events is simple: - -######Example -```php -$event = ['purchase' => ['item' => 'Golden Elephant']]; - -$client->addEvent('purchases', ['data' => $event]); -``` - -#### Send batched events to Keen -You can upload multiple Events to multiple Event Collections at once! - -In the example below, we will create two new purchase events in the `purchases` event collection and a single -new event in the `sign_ups` event collection. Note that the keys of the `data` array specify the `event_collection` -where those events should be stored. - -###### Example -```php -$purchases = [ - ['purchase' => ['item' => 'Golden Elephant']], - ['purchase' => ['item' => 'Magenta Elephant']] -]; -$signUps = [ - ['name' => 'foo', 'email' => 'bar@baz.com'] -]; - -$client->addEvents(['data' => ['purchases' => $purchases, 'sign_ups' => $signUps]]); -``` - -#### Get Analysis on Events -All Analysis Endpoints should be supported. See the [API Reference Docs](https://keen.io/docs/api/reference/) for required parameters. -You can also check the [Service Description](/src/KeenIO/Resources/config/keen-io-3_0.json) for configured API Endpoints. - -Below are a few example calls to some of the Analysis methods available. - -###### Example - -```php -//Count -$totalPurchases = $client->count('purchases'); - -//Count Unqiue -$totalItems = $client->countUnique('purchases', ['target_property' => 'purchase.item']); - -//Select Unique -$items = $client->selectUnique('purchases', ['target_property' => 'purchase.item']); - -//Multi Analysis -$analyses = [ - 'clicks' => ['analysis_type' => 'count'], - 'average price' => ['analysis_type' => 'average', 'target_property' => 'purchase.price'] -]; -$stats = $client->multiAnalysis('purchases', ['analyses' => $analyses]); -``` - -### Create a Scoped Key - -Scoped keys allow you to secure the requests to the API Endpoints and are especially useful when you are providing -access to multiple clients or applications. You should read the Keen IO docs concerning [Scoped Keys](https://keen.io/docs/security/#scoped-key) -for more details. - -######Example -```php -$filter = [ - 'property_name' => 'user_id', - 'operator' => 'eq', - 'property_value' => '123' -]; - -$filters = [$filter]; -$allowed_operations = ['read']; - -$scopedKey = $client->getScopedKey($masterKey, $filters, $allowedOperations); -``` - +Keen IO PHP Library +=================== +The Keen IO API lets developers build analytics features directly into their apps. + +[![Build Status](https://travis-ci.org/keenlabs/KeenClient-PHP.png?branch=master)](https://travis-ci.org/keenlabs/KeenClient-PHP) + +Installation with Composer +-------------------------- + 1. Install composer via via `curl -s http://getcomposer.org/installer | php` (on windows, download + http://getcomposer.org/installer and execute it with PHP) + 2. Edit your `composer.json` file with following contents: + + ```json + "require": { + "keen-io/keen-io": "~2.0" + } + ``` + 3. Run `php composer.phar install` + +Changes +------- +Please review [CHANGES.md](CHANGES.md) before upgrading! + +Usage +----- + +This client was built using [Guzzle](http://guzzlephp.org/), a PHP HTTP client & framework for building RESTful web service clients. + +When you first create a new `KeenIOClient` instance you can pass configuration settings like your Project Id and API Keys in an array +to the factory method. These are optional and can be later specified through Setter methods. + +For certain API Resources, the Master API Key is required and can also be passed to the factory method in the configuration array. +Please read the [Security Documentation](https://keen.io/docs/security/) regarding this Master API key. + +For Requests, the `KeenIOClient` will determine what API Key should be passed based on the type of Request and configuration in the +[Service Description](/src/KeenIO/Resources/config/keen-io-3_0.json). The API Key is passed in the `Authorization` header of the request. + +For a list of required and available parameters for the different API Endpoints, please consult the Keen IO +[API Reference Docs](https://keen.io/docs/api/reference/). + + +#### Configuring the Client + +The factory method accepts an array of configuration settings for the Keen IO Webservice Client. + +Setting | Property Name | Description +--- | --- | --- +Project ID | `projectId` | The Keen IO Project Id for your specific project +Master API Key | `masterKey` | The Keen IO Master API Key - the one API key to rule them all +Read API Key | `readKey` | The Read API Key - used for access to read only (GET|HEAD) operations of the API +Write API Key | `writeKey` | The Write API Key - used for write (PUT|POST Requests) operations of the API +API Version | `version` | The API Version. Currently used to version the API URL and Service Description + +When passing `version` to the factory method or using the `setVersion()` method, the Client will try to load a client Service Description +that matches that version. That Service Description defines the operations available to the Webservice Client. + +Currently the Keen IO Webservice Client only supports - and automatically defaults - to the current version (`3.0`) of the API. + +###### Example +```php +use KeenIO\Client\KeenIOClient; + +$client = KeenIOClient::factory([ + 'projectId' => $projectId, + 'writeKey' => $writeKey, + 'readKey' => $readKey +]); +``` + +#### Configuration can be updated to reuse the same Client: +You can reconfigure the Keen IO Client configuration options through available getters and setters. You can get and set the following options: +`projectId`, `readKey`, `writeKey`, `masterKey`, & `version`. + +###### Example +```php + +//Get the current Project Id +$client->getProjectId(); + +//Set a new Project Id +$client->setProjectId($someNewProjectId); + +//Get the current Read Key +$client->getReadKey(); + +//Set a new Read Key +$newReadKey = $client->getScopedKey($masterKey, $filters, $allowedOperations); +$client->setReadKey($newReadKey); + +``` + +####Send an event to Keen - ([Changed in 2.0!](CHANGE.md)) +Once you've created a `KeenIOClient`, sending events is simple: + +######Example +```php +$event = ['purchase' => ['item' => 'Golden Elephant']]; + +$client->addEvent('purchases', $event); +``` + +#### Send batched events to Keen - ([Changed in 2.0!](CHANGE.md)) +You can upload multiple Events to multiple Event Collections at once! + +In the example below, we will create two new purchase events in the `purchases` event collection and a single +new event in the `sign_ups` event collection. Note that the keys of the `data` array specify the `event_collection` +where those events should be stored. + +###### Example +```php +$purchases = [ + ['purchase' => ['item' => 'Golden Elephant']], + ['purchase' => ['item' => 'Magenta Elephant']] +]; +$signUps = [ + ['name' => 'foo', 'email' => 'bar@baz.com'] +]; + +$client->addEvents(['purchases' => $purchases, 'sign_ups' => $signUps]); +``` + +#### Get Analysis on Events +All Analysis Endpoints should be supported. See the [API Reference Docs](https://keen.io/docs/api/reference/) for required parameters. +You can also check the [Service Description](/src/KeenIO/Resources/config/keen-io-3_0.json) for configured API Endpoints. + +Below are a few example calls to some of the Analysis methods available. + +###### Example + +```php +//Count +$totalPurchases = $client->count('purchases'); + +//Count Unqiue +$totalItems = $client->countUnique('purchases', ['target_property' => 'purchase.item']); + +//Select Unique +$items = $client->selectUnique('purchases', ['target_property' => 'purchase.item']); + +//Multi Analysis +$analyses = [ + 'clicks' => ['analysis_type' => 'count'], + 'average price' => ['analysis_type' => 'average', 'target_property' => 'purchase.price'] +]; +$stats = $client->multiAnalysis('purchases', ['analyses' => $analyses]); +``` + +### Create a Scoped Key + +Scoped keys allow you to secure the requests to the API Endpoints and are especially useful when you are providing +access to multiple clients or applications. You should read the Keen IO docs concerning [Scoped Keys](https://keen.io/docs/security/#scoped-key) +for more details. + +######Example +```php +$filter = [ + 'property_name' => 'user_id', + 'operator' => 'eq', + 'property_value' => '123' +]; + +$filters = [$filter]; +$allowed_operations = ['read']; + +$scopedKey = $client->getScopedKey($masterKey, $filters, $allowedOperations); +``` diff --git a/phpunit.xml.dist b/phpunit.xml.dist index d275539..77e09cb 100755 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -8,8 +8,15 @@ processIsolation="false" stopOnFailure="false" syntaxCheck="false" - bootstrap="vendor/autoload.php" + bootstrap="tests/bootstrap.php" > + + + + + + + ./tests/ diff --git a/src/KeenIO/Client/KeenIOClient.php b/src/KeenIO/Client/KeenIOClient.php index a532231..485f3c9 100755 --- a/src/KeenIO/Client/KeenIOClient.php +++ b/src/KeenIO/Client/KeenIOClient.php @@ -15,8 +15,6 @@ * @method array getProjects(array $args = array()) {@command KeenIO getProjects} * @method array getProject(array $args = array()) {@command KeenIO getProject} * @method array getEventSchemas(array $args = array()) {@command KeenIO getEventSchemas} - * @method array addEvent(array $args = array()) {@command KeenIO addEvent} - * @method array addEvents(array $args = array()) {@command KeenIO addEvents} * @method array deleteEvents(array $args = array()) {@command KeenIO deleteEvents} * @method array deleteEventProperties(array $args = array()) {@command KeenIO deleteEventProperties} * @method array count(array $args = array()) {@command KeenIO count} @@ -56,12 +54,11 @@ public static function factory($config = array()) // Create client configuration $config = Collection::fromConfig($config, $default); - /** - * Because each API Resource uses a separate type of API Key, we need to expose them all in - * `commands.params`. Doing it this way allows the Service Definitions to set what API Key is used. - */ + + // Because each API Resource uses a separate type of API Key, we need to expose them all in + // `commands.params`. Doing it this way allows the Service Definitions to set what API Key is used. $parameters = array(); - foreach(array('masterKey', 'writeKey', 'readKey') as $key) { + foreach (array('masterKey', 'writeKey', 'readKey') as $key) { $parameters[$key] = $config->get($key); } $config->set('command.params', $parameters); @@ -81,11 +78,12 @@ public static function factory($config = array()) /** * Magic method used to retrieve a command + * * Overriden to allow the `event_collection` parameter to passed separately * from the normal argument array. * - * @param string $method Name of the command object to instantiate - * @param array $args Arguments to pass to the command + * @param string $method Name of the command object to instantiate + * @param array $args Arguments to pass to the command * * @return mixed Returns the result of the command */ @@ -94,24 +92,66 @@ public function __call($method, $args = array()) if (isset($args[0]) && is_string($args[0])) { $args[0] = array('event_collection' => $args[0]); - if (isset($args[1]) && is_array($args[1])) + if (isset($args[1]) && is_array($args[1])) { $args[0] = array_merge($args[1], $args[0]); + } } return $this->getCommand($method, isset($args[0]) ? $args[0] : array())->getResult(); } + /** + * Method used to send a single event to the Keen IO Api + * + * @param string $collection Name of the collection to store events + * @param array $event Event data to store + * + * @throws \InvalidArgumentException If $event is not an array + * @return mixed + */ + public function addEvent($collection, $event = array()) + { + if (!is_array($event)) { + $message = 'Argument 2, \'$event\' must be of the type array, ' . gettype($event) .' given.'; + throw new \InvalidArgumentException($message); + } + + $parameters = array('event_collection' => $collection, 'keen_io_event' => $event); + + return $this->getCommand('addEvent', $parameters)->getResult(); + } + + /** + * Method used to send multiple events to the Keen IO Api + * + * @param array $events Event data to store + * + * @throws \InvalidArgumentException If $events is not an array + * @return mixed + */ + public function addEvents($events = array()) + { + if ( !is_array( $events ) ) { + $message = 'Argument 1, \'$events\' must be of the type array, ' . gettype($events) . ' given.'; + throw new \InvalidArgumentException($message); + } + + $parameters = array('keen_io_events' => $events); + + return $this->getCommand('addEvents', $parameters)->getResult(); + } + /** * Get a scoped key for an array of filters * - * @param string $apiKey The master API key to use for encryption - * @param array $filters What filters to encode into a scoped key - * @param array $allowedOperations What operations the generated scoped key will allow - * @param int $source + * @param string $apiKey The master API key to use for encryption + * @param array $filters What filters to encode into a scoped key + * @param array $allowedOperations What operations the generated scoped key will allow + * @param int $source * * @return string */ - public function getScopedKey( $apiKey, $filters, $allowedOperations, $source = MCRYPT_DEV_RANDOM ) + public function getScopedKey($apiKey, $filters, $allowedOperations, $source = MCRYPT_DEV_RANDOM) { $options = array( 'filters' => $filters ); @@ -137,8 +177,8 @@ public function getScopedKey( $apiKey, $filters, $allowedOperations, $source = M /** * Implement PKCS7 padding * - * @param string $string - * @param int $blockSize + * @param string $string + * @param int $blockSize * * @return string */ @@ -153,8 +193,8 @@ protected function padString($string, $blockSize = 32) /** * Decrypt a scoped key (primarily used for testing) * - * @param string $apiKey The master API key to use for decryption - * @param string $scopedKey The scoped Key to decrypt + * @param string $apiKey The master API key to use for decryption + * @param string $scopedKey The scoped Key to decrypt * @return mixed */ public function decryptScopedKey($apiKey, $scopedKey) @@ -178,7 +218,7 @@ public function decryptScopedKey($apiKey, $scopedKey) /** * Remove padding for a PKCS7-padded string * - * @param string $string + * @param string $string * @return string */ protected function unpadString($string) @@ -326,12 +366,11 @@ public function getVersion() * Validates the Keen IO Client configuration options * * @params array $config - * @throws \InvalidArgumentException When a config value does not meet its validation criteria + * @throws \InvalidArgumentException When a config value does not meet its validation criteria */ - static function validateConfig($config = array()) + public static function validateConfig($config = array()) { - foreach($config as $option => $value) - { + foreach ($config as $option => $value) { if ($option === 'version' && empty($config['version'])) { throw new \InvalidArgumentException("Version can not be empty"); } diff --git a/src/KeenIO/Resources/config/keen-io-3_0.json b/src/KeenIO/Resources/config/keen-io-3_0.json index 3b6eec8..c69e8d8 100755 --- a/src/KeenIO/Resources/config/keen-io-3_0.json +++ b/src/KeenIO/Resources/config/keen-io-3_0.json @@ -68,7 +68,7 @@ "description": "The event collection.", "required": true }, - "data": { + "keen_io_event": { "location": "body", "type": "array", "filters": [ "json_encode" ] @@ -86,7 +86,7 @@ "sentAs": "Authorization", "required": true }, - "data": { + "keen_io_events": { "location": "body", "type": "array", "filters": [ "json_encode" ] diff --git a/tests/KeenIOClientTest.php b/tests/KeenIOClientTest.php deleted file mode 100755 index cc87357..0000000 --- a/tests/KeenIOClientTest.php +++ /dev/null @@ -1,302 +0,0 @@ - $projectId, - 'masterKey' => $masterKey, - 'readKey' => $readKey, - 'writeKey' => $writeKey, - 'version' => $version - ); - - $client = KeenIOClient::factory( $config ); - - //Check that the Client is of the right type - $this->assertInstanceOf( '\KeenIO\Client\KeenIOClient', $client ); - - //Check that the pass config options match the client's config - $this->assertEquals( $config['projectId'], $client->getConfig('projectId') ); - $this->assertEquals( $config['masterKey'], $client->getConfig('masterKey') ); - $this->assertEquals( $config['readKey'], $client->getConfig('readKey') ); - $this->assertEquals( $config['writeKey'], $client->getConfig('writeKey') ); - } - - /** - * @expectedException InvalidArgumentException - */ - public function testFactoryReturnsExceptionOnInvalidProjectId() - { - $config = array( 'projectId' => '!#1234567890.' ); - - $client = KeenIOClient::factory( $config ); - } - - /** - * @expectedException InvalidArgumentException - */ - public function testFactoryReturnsExceptionOnBlankProjectId() - { - $config = array( 'projectId' => '' ); - - $client = KeenIOClient::factory( $config ); - } - - public function testProjectIdSetter() - { - $projectId = "testProjectId"; - $client = KeenIOClient::factory( array() ); - - $client->setProjectId( $projectId ); - - $this->assertEquals( $projectId, $client->getConfig('projectId') ); - } - - /** - * @expectedException InvalidArgumentException - */ - public function testSetterReturnsExceptionOnInvalidProjectId() - { - $client = KeenIOClient::factory( array() ); - - $client->setProjectId( '!#1234567890.' ); - } - - /** - * @expectedException InvalidArgumentException - */ - public function testSetterReturnsExceptionOnBlankProjectId() - { - $client = KeenIOClient::factory( array() ); - - $client->setProjectId( '' ); - } - - /** - * @expectedException InvalidArgumentException - */ - public function testFactoryReturnsExceptionOnInvalidMasterKey() - { - $config = array( 'masterKey' => '!#1234567890.' ); - - $client = KeenIOClient::factory( $config ); - } - - /** - * @expectedException InvalidArgumentException - */ - public function testFactoryReturnsExceptionOnBlankMasterKey() - { - $config = array( 'masterKey' => '' ); - - $client = KeenIOClient::factory( $config ); - } - - public function testMasterKeySetter() - { - $masterKey = "testMasterKey"; - $client = KeenIOClient::factory( array() ); - - $client->setMasterKey( $masterKey ); - - $this->assertEquals( $masterKey, $client->getConfig('masterKey') ); - } - - /** - * @expectedException InvalidArgumentException - */ - public function testSetterReturnsExceptionOnInvalidMasterKey() - { - $client = KeenIOClient::factory( array() ); - - $client->setMasterKey( '!#1234567890.' ); - } - - /** - * @expectedException InvalidArgumentException - */ - public function testSetterReturnsExceptionOnBlankMasterKey() - { - $client = KeenIOClient::factory( array() ); - - $client->setMasterKey( '' ); - } - - /** - * @expectedException InvalidArgumentException - */ - public function testFactoryReturnsExceptionOnInvalidReadKey() - { - $config = array( 'readKey' => '!#1234567890.' ); - - $client = KeenIOClient::factory( $config ); - } - - /** - * @expectedException InvalidArgumentException - */ - public function testFactoryReturnsExceptionOnBlankReadKey() - { - $config = array( 'readKey' => '' ); - - $client = KeenIOClient::factory( $config ); - } - - public function testReadKeySetter() - { - $readKey = "testReadKey"; - $client = KeenIOClient::factory( array() ); - - $client->setReadKey( $readKey ); - - $this->assertEquals( $readKey, $client->getConfig('readKey') ); - } - - /** - * @expectedException InvalidArgumentException - */ - public function testSetterReturnsExceptionOnInvalidReadKey() - { - $client = KeenIOClient::factory( array() ); - - $client->setReadKey( '!#1234567890.' ); - } - - /** - * @expectedException InvalidArgumentException - */ - public function testSetterReturnsExceptionOnBlankReadKey() - { - $client = KeenIOClient::factory( array() ); - - $client->setReadKey( '' ); - } - - /** - * @expectedException InvalidArgumentException - */ - public function testFactoryReturnsExceptionOnInvalidWriteKey() - { - $config = array( 'writeKey' => '!#1234567890.' ); - - $client = KeenIOClient::factory( $config ); - } - - /** - * @expectedException InvalidArgumentException - */ - public function testFactoryReturnsExceptionOnBlankWriteKey() - { - $config = array( 'writeKey' => '' ); - - $client = KeenIOClient::factory( $config ); - } - - public function testWriteKeySetter() - { - $writeKey = "testWriteKey"; - $client = KeenIOClient::factory( array() ); - - $client->setWriteKey( $writeKey ); - - $this->assertEquals( $writeKey, $client->getConfig('writeKey') ); - } - - /** - * @expectedException InvalidArgumentException - */ - public function testSetterReturnsExceptionOnInvalidWriteKey() - { - $client = KeenIOClient::factory( array() ); - - $client->setWriteKey( '!#1234567890.' ); - } - - /** - * @expectedException InvalidArgumentException - */ - public function testSetterReturnsExceptionOnBlankWriteKey() - { - $client = KeenIOClient::factory( array() ); - - $client->setWriteKey( '' ); - } - - /** - * @expectedException InvalidArgumentException - */ - public function testFactoryReturnsExceptionOnBlankVersion() - { - $config = array( 'version' => '' ); - - $client = KeenIOClient::factory( $config ); - } - - public function testVersionSetter() - { - $version = "3.0"; - $client = KeenIOClient::factory( array() ); - - $client->setVersion( $version ); - - $this->assertEquals( $version, $client->getConfig('version') ); - } - - /** - * @expectedException InvalidArgumentException - */ - public function testSetterReturnsExceptionOnBadVersion() - { - $version = '99.0'; - $client = KeenIOClient::factory( array() ); - - $client->setVersion( $version ); - } - - /** - * @expectedException InvalidArgumentException - */ - public function testSetterReturnsExceptionOnBlankVersion() - { - $client = KeenIOClient::factory( array() ); - - $client->setVersion( '' ); - } - - public function testGetScopedKey() - { - $client = KeenIOClient::factory(); - - $apiKey = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; - $filter = array( 'property_name' => 'id', 'operator' => 'eq', 'property_value' => '123' ); - $filters = array( $filter ); - $allowed_operations = array( 'read' ); - - $scopedKey = $client->getScopedKey( $apiKey, $filters, $allowed_operations ); - - $result = $client->decryptScopedKey( $apiKey, $scopedKey ); - $expected = array( 'filters' => $filters, 'allowed_operations' => $allowed_operations ); - - $this->assertEquals( $expected, $result ); - } - - public function provideConfigValues() - { - return array( array( 'testMasterAPIKey', 'testReadAPIKey', 'testWriteAPIKey', 'testProjectId', '3.0' ) ); - } -} diff --git a/tests/Tests/Client/KeenIOClientTest.php b/tests/Tests/Client/KeenIOClientTest.php new file mode 100755 index 0000000..16916f9 --- /dev/null +++ b/tests/Tests/Client/KeenIOClientTest.php @@ -0,0 +1,212 @@ + 'testProjectId', + 'masterKey' => 'testMasterKey', + 'readKey' => 'testMasterKey', + 'writeKey' => 'testWriteKey', + 'version' => '3.0' + ); + + $client = KeenIOClient::factory($config); + + //Check that the Client is of the right type + $this->assertInstanceOf('\Guzzle\Service\Client', $client); + $this->assertInstanceOf('\KeenIO\Client\KeenIOClient', $client); + + //Check that the pass config options match the client's config + $this->assertEquals($config['projectId'], $client->getConfig('projectId')); + $this->assertEquals($config['masterKey'], $client->getConfig('masterKey')); + $this->assertEquals($config['readKey'], $client->getConfig('readKey')); + $this->assertEquals($config['writeKey'], $client->getConfig('writeKey')); + } + + /** + * @dataProvider invalidClientConfigValues + * @expectedException InvalidArgumentException + */ + public function testFactoryReturnsExceptionOnInvalidConfigs($masterKey, $readKey, $writeKey, $projectId, $version) + { + $config = array( + 'projectId' => $projectId, + 'masterKey' => $masterKey, + 'readKey' => $readKey, + 'writeKey' => $writeKey, + 'version' => $version + ); + + $client = KeenIOClient::factory($config); + } + + /** + * Tests the client setter method and that the value returned is correct + */ + public function testProjectIdSetter() + { + $client = $this->getServiceBuilder()->get('keen-io'); + $client->setProjectId('testProjectId'); + + $this->assertEquals('testProjectId', $client->getConfig('projectId')); + } + + /** + * @dataProvider invalidValues + * @expectedException InvalidArgumentException + */ + public function testSetterReturnsExceptionOnInvalidProjectId($projectId) + { + $client = $this->getServiceBuilder()->get('keen-io'); + $client->setProjectId($projectId); + } + + /** + * Tests the client setter method and that the value returned is correct + */ + public function testReadKeySetter() + { + $client = $this->getServiceBuilder()->get('keen-io'); + $client->setReadKey('testReadKey'); + + $this->assertEquals('testReadKey', $client->getConfig('readKey')); + } + + /** + * @dataProvider invalidValues + * @expectedException InvalidArgumentException + */ + public function testSetterReturnsExceptionOnInvalidReadKey($readKey) + { + $client = $this->getServiceBuilder()->get('keen-io'); + $client->setReadKey($readKey); + } + + /** + * Tests the client setter method and that the value returned is correct + */ + public function testWriteKeySetter() + { + $client = $this->getServiceBuilder()->get('keen-io'); + $client->setWriteKey('testWriteKey'); + + $this->assertEquals('testWriteKey', $client->getConfig('writeKey')); + } + + /** + * @dataProvider invalidValues + * @expectedException InvalidArgumentException + */ + public function testSetterReturnsExceptionOnInvalidWriteKey($writeKey) + { + $client = $this->getServiceBuilder()->get('keen-io'); + $client->setWriteKey($writeKey); + } + + /** + * Tests the client setter method and that the value returned is correct + */ + public function testMasterKeySetter() + { + $client = $this->getServiceBuilder()->get('keen-io'); + $client->setMasterKey('testMasterKey'); + + $this->assertEquals('testMasterKey', $client->getConfig('masterKey')); + } + + /** + * @dataProvider invalidValues + * @expectedException InvalidArgumentException + */ + public function testSetterReturnsExceptionOnInvalidMasterKey($masterKey) + { + $client = $this->getServiceBuilder()->get('keen-io'); + $client->setMasterKey($masterKey); + } + + /** + * Tests the client setter method and that the value returned is correct + */ + public function testVersionSetter() + { + $client = $this->getServiceBuilder()->get('keen-io'); + $client->setVersion('3.0'); + + $this->assertEquals('3.0', $client->getConfig('version')); + } + + /** + * @dataProvider invalidVersions + * @expectedException InvalidArgumentException + */ + public function testSetterReturnsExceptionOnInvalidVersion($version) + { + $client = KeenIOClient::factory(array()); + + $client->setVersion($version); + } + + /** + * Tests the creation of a Scoped Key + */ + public function testGetScopedKey() + { + $client = KeenIOClient::factory(); + + $apiKey = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; + $filter = array('property_name' => 'id', 'operator' => 'eq', 'property_value' => '123'); + $filters = array($filter); + $allowed_operations = array('read'); + + $scopedKey = $client->getScopedKey($apiKey, $filters, $allowed_operations); + + $result = $client->decryptScopedKey($apiKey, $scopedKey); + $expected = array('filters' => $filters, 'allowed_operations' => $allowed_operations); + + $this->assertEquals($expected, $result); + } + + /** + * Invalid values used for testing setter methods on the Keen IO Client + */ + public function invalidValues() + { + return array(array('!#1234567890.'), array('')); + } + + /** + * Invalid version values used for testing version setter + */ + public function invalidVersions() + { + return array(array(''), array('99.0')); + } + + /** + * Invalid config values used for testing the factory method of the Keen IO Client + */ + public function invalidClientConfigValues() + { + return array( + array('!#1234567890.', 'testReadAPIKey', 'testWriteAPIKey', 'testProjectId', '3.0'), // Invalid Master Key + array('', 'testReadAPIKey', 'testWriteAPIKey', 'testProjectId', '3.0'), // Blank Master Key + array('testMasterAPIKey', '!#1234567890.', 'testWriteAPIKey', 'testProjectId', '3.0'), // Invalid Read Key + array('testMasterAPIKey', '', 'testWriteAPIKey', 'testProjectId', '3.0'), // Blank Read Key + array('testMasterAPIKey', 'testReadAPIKey', '!#1234567890.', 'testProjectId', '3.0'), // Invalid Write Key + array('testMasterAPIKey', 'testReadAPIKey', '', 'testProjectId', '3.0'), // Blank Write Key + array('testMasterAPIKey', 'testReadAPIKey', 'testWriteAPIKey', '!#1234567890.', '3.0'), // Invalid Project Id + array('testMasterAPIKey', 'testReadAPIKey', 'testWriteAPIKey', '', '3.0'), // Blank Project Id + array('testMasterAPIKey', 'testReadAPIKey', 'testWriteAPIKey', 'testProjectId', '') // Blank Version + ); + } +} diff --git a/tests/Tests/Client/KeenIOServiceMethodsTest.php b/tests/Tests/Client/KeenIOServiceMethodsTest.php new file mode 100755 index 0000000..af1aad9 --- /dev/null +++ b/tests/Tests/Client/KeenIOServiceMethodsTest.php @@ -0,0 +1,183 @@ +getServiceBuilder()->get('keen-io'); + + $this->setMockResponse($client, 'valid-response.mock'); + $result = $client->getCommand($method, $params)->getResult(); + + $requests = $this->getMockedRequests(); + + //Resource Url + $url = parse_url($requests[0]->getUrl()); + parse_str($url['query'], $queryString); + + //Camel to underscore case + $method = strtolower(preg_replace('/([a-z])([A-Z])/', '$1_$2', $method)); + + //Make sure the projectId is set properly in the url + $this->assertContains($client->getProjectId(), explode('/', $url['path'])); + + //Make sure the version is set properly in the url + $this->assertContains($client->getVersion(), explode('/', $url['path'])); + + //Make sure the url has the right method + $this->assertContains($method, explode('/', $url['path'])); + + //Check that the querystring has all the parameters + $this->assertEquals($params, $queryString); + } + + /** + * @dataProvider providerServiceCommands + * @expectedException \Guzzle\Http\Exception\ClientErrorResponseException + */ + public function testServiceCommandsReturnExceptionOnInvalidAuth($method, $params) + { + $client = $this->getServiceBuilder()->get('keen-io'); + + $this->setMockResponse($client, 'invalid-auth.mock'); + $result = $client->getCommand($method, $params)->getResult(); + } + + /** + * @dataProvider providerServiceCommands + * @expectedException \Guzzle\Http\Exception\ClientErrorResponseException + */ + public function testServiceCommandsReturnExceptionOnInvalidProjectId($method, $params) + { + $client = $this->getServiceBuilder()->get('keen-io'); + + $this->setMockResponse($client, 'invalid-project-id.mock'); + $result = $client->getCommand($method, $params)->getResult(); + } + + /** + * Uses mock response to test addEvent service method. Also checks that event data + * is properly json_encoded in the request body. + */ + public function testSendEventMethod() + { + $event = array('foo' => 'bar', 'baz' => 1); + + $client = $this->getServiceBuilder()->get('keen-io'); + + $this->setMockResponse($client, 'add-event.mock'); + $response = $client->addEvent('test', $event); + $requests = $this->getMockedRequests(); + + //Resource Url + $url = parse_url($requests[0]->getUrl()); + + $expectedResponse = array('created' => true); + + //Make sure the projectId is set properly in the url + $this->assertContains($client->getProjectId(), explode('/', $url['path'])); + + //Make sure the version is set properly in the url + $this->assertContains($client->getVersion(), explode('/', $url['path'])); + + //Checks that the response is good - based off mock response + $this->assertJsonStringEqualsJsonString(json_encode($expectedResponse), json_encode($response)); + + //Checks that the event is properly encoded in the request body + $this->assertJsonStringEqualsJsonString(json_encode($event), (string) $requests[0]->getBody()); + } + + /** + * @dataProvider providerInvalidEvents + * @expectedException InvalidArgumentException + */ + public function testSendEventReturnsExceptionOnBadDataType($event) + { + $client = $this->getServiceBuilder()->get('keen-io'); + + $this->setMockResponse($client, 'add-event.mock'); + $response = $client->addEvent('test', $event); + } + + /** + * Uses mock response to test addEvents service method. Also checks that event data + * is properly json_encoded in the request body. + */ + public function testSendEventsMethod() + { + $events = array('test' => array(array('foo' => 'bar'), array('bar' => 'baz'))); + + $client = $this->getServiceBuilder()->get('keen-io'); + + $this->setMockResponse($client, 'add-events.mock'); + $response = $client->addEvents($events); + $requests = $this->getMockedRequests(); + + //Resource Url + $url = parse_url($requests[0]->getUrl()); + + $expectedResponse = array('test' => array(array('success' => true), array('success'=>true))); + + //Make sure the projectId is set properly in the url + $this->assertContains($client->getProjectId(), explode('/', $url['path'])); + + //Make sure the version is set properly in the url + $this->assertContains($client->getVersion(), explode('/', $url['path'])); + + //Checks that the response is good - based off mock response + $this->assertJsonStringEqualsJsonString(json_encode($expectedResponse), json_encode($response)); + + //Checks that the event is properly encoded in the request body + $this->assertJsonStringEqualsJsonString(json_encode($events), (string) $requests[0]->getBody()); + } + + /** + * @dataProvider providerInvalidEvents + * @expectedException InvalidArgumentException + */ + public function testSendEventsReturnsExceptionOnBadDataType($events) + { + $client = $this->getServiceBuilder()->get('keen-io'); + + $this->setMockResponse($client, 'add-events.mock'); + $response = $client->addEvents($events); + } + + /** + * Invalid data types for events + */ + public function providerInvalidEvents() + { + $obj = new \stdClass(); + + return array( + array($obj), + array('string'), + array(12345), + ); + } + + /** + * Data for service calls + */ + public function providerServiceCommands() + { + return array( + array('count', array('event_collection' => 'test', 'timeframe' => 'this_week')), + array('countUnique', array('event_collection' => 'test', 'target_property' => 'foo', 'timeframe' => 'this_week')), + array('minimum', array('event_collection' => 'test', 'target_property' => 'foo', 'timeframe' => 'this_week')), + array('maximum', array('event_collection' => 'test', 'target_property' => 'foo', 'timeframe' => 'this_week')), + array('average', array('event_collection' => 'test', 'target_property' => 'foo', 'timeframe' => 'this_week')), + array('sum', array('event_collection' => 'test', 'target_property' => 'foo', 'timeframe' => 'this_week')), + array('selectUnique', array('event_collection' => 'test', 'target_property' => 'foo', 'timeframe' => 'this_week')), + array('extraction', array('event_collection' => 'test', 'timeframe' => 'this_week', 'latest' => 10)) + ); + } +} diff --git a/tests/bootstrap.php b/tests/bootstrap.php new file mode 100644 index 0000000..c5a2080 --- /dev/null +++ b/tests/bootstrap.php @@ -0,0 +1,35 @@ +add('KeenIO\\Tests', __DIR__); + +// Register services with the GuzzleTestCase +Guzzle\Tests\GuzzleTestCase::setMockBasePath(__DIR__ . '/mock'); + +$serviceBuilder = \Guzzle\Service\Builder\ServiceBuilder::factory(array( + 'services' => array( + 'keen-io' => array( + 'class' => 'KeenIO\Client\KeenIOClient', + 'params' => array( + 'projectId' => $_SERVER['PROJECT_ID'], + 'masterKey' => $_SERVER['MASTER_KEY'], + 'writeKey' => $_SERVER['WRITE_KEY'], + 'readKey' => $_SERVER['READ_KEY'], + 'version' => $_SERVER['API_VERSION'] + ) + ) + ) +) ); + +Guzzle\Tests\GuzzleTestCase::setServiceBuilder( $serviceBuilder ); + +// Emit deprecation warnings +Guzzle\Common\Version::$emitWarnings = true; diff --git a/tests/mock/add-event.mock b/tests/mock/add-event.mock new file mode 100644 index 0000000..ee2fc56 --- /dev/null +++ b/tests/mock/add-event.mock @@ -0,0 +1,14 @@ +HTTP/1.1 201 Created +Server: Keen/K9-D/1.5x25-alpha3 +Date: Fri, 29 Nov 2013 06:27:29 GMT +Content-Type: application/json +Content-Length: 17 +Connection: close, close +Expires: Sat, 01 Jan 2000 01:01:01 GMT +Vary: Accept-Encoding +Pragma: no-cache +Cache-Control: private, no-cache, no-cache=Set-Cookie, max-age=0, s-maxage=0 +Access-Control-Allow-Origin: * +Access-Control-Allow-Headers: origin, content-type, accept, authorization, user-agent + +{"created": true} diff --git a/tests/mock/add-events.mock b/tests/mock/add-events.mock new file mode 100644 index 0000000..038157b --- /dev/null +++ b/tests/mock/add-events.mock @@ -0,0 +1,14 @@ +HTTP/1.1 200 OK +Server: Keen/K9-D/1.5x25-alpha3 +Date: Fri, 29 Nov 2013 06:51:41 GMT +Content-Type: application/json +Content-Length: 58 +Connection: close, close +Expires: Sat, 01 Jan 2000 01:01:01 GMT +Vary: Accept-Encoding +Pragma: no-cache +Cache-Control: private, no-cache, no-cache=Set-Cookie, max-age=0, s-maxage=0 +Access-Control-Allow-Origin: * +Access-Control-Allow-Headers: origin, content-type, accept, authorization, user-agent + +{"test": [{"success": true}, {"success": true}]} diff --git a/tests/mock/invalid-auth.mock b/tests/mock/invalid-auth.mock new file mode 100644 index 0000000..955f158 --- /dev/null +++ b/tests/mock/invalid-auth.mock @@ -0,0 +1,11 @@ +HTTP/1.1 401 Unauthorized +Server: Keen/K9-D/1.5x25-alpha3 +Date: Sat, 30 Nov 2013 05:59:17 GMT +Content-Type: application/json +Content-Length: 315 +Connection: close +Vary: Accept-Encoding +Access-Control-Allow-Origin: * +Access-Control-Allow-Headers: origin, content-type, accept, authorization, user-agent + +{"message": "Specified API Key is invalid. API Key: 'testApiKey'.", "error_code": "InvalidApiKeyError"} diff --git a/tests/mock/invalid-project-id.mock b/tests/mock/invalid-project-id.mock new file mode 100644 index 0000000..c611179 --- /dev/null +++ b/tests/mock/invalid-project-id.mock @@ -0,0 +1,11 @@ +HTTP/1.1 404 Not Found +Server: Keen/K9-D/1.5x25-alpha3 +Date: Fri, 29 Nov 2013 20:46:58 GMT +Content-Type: application/json +Content-Length: 73 +Connection: close +Vary: Accept-Encoding +Access-Control-Allow-Origin: * +Access-Control-Allow-Headers: origin, content-type, accept, authorization, user-agent + +{"message": "Resource not found.", "error_code": "ResourceNotFoundError"} diff --git a/tests/mock/valid-response.mock b/tests/mock/valid-response.mock new file mode 100644 index 0000000..c600ea4 --- /dev/null +++ b/tests/mock/valid-response.mock @@ -0,0 +1,15 @@ +HTTP/1.1 200 OK +Server: Keen/K9-D/1.5x25-alpha3 +Date: Sat, 30 Nov 2013 04:26:53 GMT +Content-Type: application/json +Content-Length: 18 +Connection: close, close +Expires: Sat, 01 Jan 2000 01:01:01 GMT +Vary: Accept-Encoding +Etag: "01d58e2ee345954dd8a8b933678921d5942d600f" +Pragma: no-cache +Cache-Control: private, no-cache, no-cache=Set-Cookie, max-age=0, s-maxage=0 +Access-Control-Allow-Origin: * +Access-Control-Allow-Headers: origin, content-type, accept, authorization, user-agent + +{"response": true}