diff --git a/.example.env b/.example.env new file mode 100644 index 0000000..6dfbfd2 --- /dev/null +++ b/.example.env @@ -0,0 +1,14 @@ +GA_TID=G-XXXXXXXXXX +GA_CID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + +PA_DOMAIN=example.com +PA_APIKEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + +OR_WORKSPACEID=orbxxxxxxxxxx +OR_APIKEY=orbk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + +MP_PROJECT_TOKEN=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + +HS_APIKEY=pat-na1-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + +REO_APIKEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx \ No newline at end of file diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index ce6186a..da9ce90 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -11,7 +11,7 @@ jobs: services: [ Plausible, - HubSpot + ReoDev ] steps: @@ -29,6 +29,7 @@ jobs: echo "HS_APIKEY=${{ secrets.HS_APIKEY }}" >> .env echo "OR_APIKEY=${{ secrets.OR_APIKEY }}" >> .env echo "OR_WORKSPACEID=${{ secrets.OR_WORKSPACEID }}" >> .env + echo "REO_APIKEY=${{ secrets.REO_APIKEY }}" >> .env - name: Run ${{matrix.services}} Tests run: | diff --git a/README.md b/README.md index fb35335..4d50063 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,22 @@ # Utopia Analytics -[![Build Status](https://travis-ci.com/utopia-php/system.svg?branch=main)](https://travis-ci.com/utopia-php/analytics) ![Total Downloads](https://img.shields.io/packagist/dt/utopia-php/analytics.svg) [![Discord](https://img.shields.io/discord/564160730845151244?label=discord)](https://appwrite.io/discord) -Utopia Analytics is a simple and lite library to send information about events or pageviews to Google Analytics. This library is aiming to be as simple and easy to learn and use. This library is maintained by the [Appwrite team](https://appwrite.io). +Utopia Analytics is a simple and lite library to send information about events or pageviews to various analytics platforms. This library is aiming to be as simple and easy to learn and use. This library is maintained by the [Appwrite team](https://appwrite.io). Although this library is part of the [Utopia Framework](https://github.com/utopia-php/framework) project it is dependency free and can be used as standalone with any other PHP project or framework. ## Getting Started Install using composer: + ```bash composer require utopia-php/analytics ``` Init in your application: + ```php createPageView("appwrite.io", "/docs/installation"); // Sends an event indicating an installation. $ga->createEvent("Installation", "setup.cli"); + // Sends an event indicating that the fall campaign promotional video was played. $ga->createEvent("Videos", "play", "Fall Campaign"); ``` +## Supported Platforms + +- [Google Analytics](https://analytics.google.com) +- [Plausible](https://plausible.io) +- [Mixpanel](https://mixpanel.com) +- [HubSpot](https://hubspot.com) +- [Orbit](https://orbit.love) +- [Reo.dev](https://reo.dev) + ## System Requirements Utopia Framework requires PHP 7.4 or later. We recommend using the latest PHP version whenever possible. diff --git a/src/Analytics/Adapter/ReoDev.php b/src/Analytics/Adapter/ReoDev.php new file mode 100644 index 0000000..740bfb3 --- /dev/null +++ b/src/Analytics/Adapter/ReoDev.php @@ -0,0 +1,117 @@ +apiKey = $apiKey; + } + + /** + * Set the event types that should be sent. + * + * @param array $types An array of event type strings. + */ + public function setAllowedEventTypes(array $types): self + { + $this->allowedEventTypes = $types; + + return $this; + } + + /** + * Sends an event to the ReoDev Product API. Requires 'email' prop and checks against allowed types. + */ + public function send(Event $event): bool + { + if (! $this->validate($event)) { + return false; + } + + $meta = $event->getProps(); + unset($meta['email']); + unset($meta['account']); + $payload = [ + 'activity_type' => $event->getType(), + 'source' => 'PRODUCT_CLOUD', + 'environment' => $event->getProp('environment'), + 'user_id' => $event->getProp('email'), + 'user_id_type' => 'EMAIL', + 'ip_addr' => $this->clientIP, + 'event_at' => time(), + 'product_id' => $event->getProp('account'), + 'user_agent' => $this->userAgent, + 'meta' => $meta, + ]; + + $payload = array_filter($payload, fn ($value) => ! is_null($value)); + + $body = ['payload' => $payload]; + + $this->call('POST', $this->endpoint, [ + 'Content-Type' => 'application/json', + 'X-API-KEY' => $this->apiKey, + ], $body); + + return true; + } + + /** + * Validates the event. + * + * Checks if: + * - the event has an 'email' property + * - the event type is allowed (if a filter is set) + * - the environment is either PRODUCTION or DEVELOPMENT + * + * @param Event $event The event to validate. + */ + public function validate(Event $event): bool + { + if (empty($event->getProp('email'))) { + return false; + } + + if (! empty($this->allowedEventTypes) && ! in_array($event->getType(), $this->allowedEventTypes)) { + return false; + } + + $environment = $event->getProp('environment'); + if (! in_array($environment, ['PRODUCTION', 'DEVELOPMENT'])) { + return false; + } + + return true; + } +} diff --git a/tests/Analytics/AnalyticsTest.php b/tests/Analytics/AnalyticsTest.php index 395391f..28f3720 100644 --- a/tests/Analytics/AnalyticsTest.php +++ b/tests/Analytics/AnalyticsTest.php @@ -8,6 +8,7 @@ use Utopia\Analytics\Adapter\Mixpanel; use Utopia\Analytics\Adapter\Orbit; use Utopia\Analytics\Adapter\Plausible; +use Utopia\Analytics\Adapter\ReoDev; use Utopia\Analytics\Event; use Utopia\System\System; @@ -28,6 +29,9 @@ class AnalyticsTest extends TestCase /** @var \Utopia\Analytics\Adapter\HubSpot */ public $hs; + /** @var \Utopia\Analytics\Adapter\ReoDev */ + public $reodev; + public function setUp(): void { $this->ga = new GoogleAnalytics(System::getEnv('GA_TID') ?? '', System::getEnv('GA_CID') ?? ''); @@ -35,6 +39,7 @@ public function setUp(): void $this->orbit = new Orbit(System::getEnv('OR_WORKSPACEID') ?? '', System::getEnv('OR_APIKEY') ?? '', 'Utopia Testing Suite'); $this->mp = new Mixpanel(System::getEnv('MP_PROJECT_TOKEN') ?? ''); $this->hs = new HubSpot(System::getEnv('HS_APIKEY') ?? ''); + $this->reodev = new ReoDev(System::getEnv('REO_APIKEY') ?? ''); } /** @@ -246,7 +251,7 @@ public function testCleanup(): void } /** - * @group mixpanel + * @group Mixpanel */ public function testMixpanel() { @@ -282,4 +287,66 @@ public function testMixpanel() $res = $this->mp->appendProperties('analytics@utopiaphp.com', ['union_field' => ['value2', 'value3']]); $this->assertTrue($res); } + + /** + * @group ReoDev + */ + public function testReoDev() + { + $this->reodev + ->setClientIP('127.0.0.1') + ->setUserAgent('Utopia Test Suite'); + + // Test successful event with all required fields and no filter + $event = new Event; + $event + ->setName('appwrite_docs') + ->setType('button_click') + ->setUrl('appwrite.io/docs') + ->setProps([ + 'email' => 'developer@utopiaphp.com', + 'name' => 'Test Developer', + 'account' => 'cloud', + 'environment' => 'DEVELOPMENT', + 'custom_prop1' => 'value1', + 'custom_prop2' => 'value2', + ]); + + $this->assertTrue($this->reodev->validate($event)); + $this->assertTrue($this->reodev->send($event)); + + // Test event without email (should fail validation and send) + $invalidEvent = new Event; + $invalidEvent + ->setName('appwrite_docs') + ->setType('page_view') // Use a different type for clarity + ->setUrl('appwrite.io/docs') + ->setProps([ + 'name' => 'Test Developer', + 'account' => 'cloud', + 'environment' => 'DEVELOPMENT', + ]); + + $this->assertFalse($this->reodev->validate($invalidEvent)); + $this->assertFalse($this->reodev->send($invalidEvent)); + + // Test event type filtering + $allowedTypes = ['submit_account_login']; + $this->reodev->setAllowedEventTypes($allowedTypes); + + // Disallowed event + $disallowedEvent = new Event; + $disallowedEvent + ->setName('signup') + ->setType('submit_signup') + ->setUrl('appwrite.io/signup') + ->setProps([ + 'email' => 'dev3@utopiaphp.com', + 'account' => 'cloud', + 'environment' => 'DEVELOPMENT', + ]); + + $this->assertFalse($this->reodev->validate($disallowedEvent)); + $this->assertFalse($this->reodev->send($disallowedEvent)); + } }