From 52021eef254ac5a04947a9ea81c91c796f63d71c Mon Sep 17 00:00:00 2001 From: wess Date: Wed, 19 Jul 2023 09:36:11 -0400 Subject: [PATCH 01/39] Added start to APNS --- .gitignore | 1 + src/Utopia/Messaging/Adapters/Push/APNS.php | 33 +++++++++++++ src/Utopia/Messaging/Apple/Notification.php | 51 +++++++++++++++++++++ 3 files changed, 85 insertions(+) create mode 100644 src/Utopia/Messaging/Adapters/Push/APNS.php create mode 100644 src/Utopia/Messaging/Apple/Notification.php diff --git a/.gitignore b/.gitignore index 24265e89..7539ebce 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .idea vendor .phpunit.result.cache +Makefile \ No newline at end of file diff --git a/src/Utopia/Messaging/Adapters/Push/APNS.php b/src/Utopia/Messaging/Adapters/Push/APNS.php new file mode 100644 index 00000000..0cce5fd8 --- /dev/null +++ b/src/Utopia/Messaging/Adapters/Push/APNS.php @@ -0,0 +1,33 @@ +client = new ApplePushNotification($certificatePath, $passphrase, $ssl); + } + + public function getName(): string + { + return 'applePush'; + } + + public function process(Push $message): string + { + try { + $this->client->send($message->getTo(), $message->getBody()); + + return true; + } catch (\Exception $e) { + throw new \Exception($e->getMessage(), 500); + } + } +} + +?> diff --git a/src/Utopia/Messaging/Apple/Notification.php b/src/Utopia/Messaging/Apple/Notification.php new file mode 100644 index 00000000..7821f7f8 --- /dev/null +++ b/src/Utopia/Messaging/Apple/Notification.php @@ -0,0 +1,51 @@ +certificatePath = $certificatePath; + $this->passphrase = $passphrase; + $this->ssl = $ssl; + } + + public function send($deviceToken, $message) + { + $ctx = stream_context_create(); + stream_context_set_option($ctx, 'ssl', 'local_cert', $this->certificatePath); + stream_context_set_option($ctx, 'ssl', 'passphrase', $this->passphrase); + + $fp = stream_socket_client( + $this->ssl, $err, + $errstr, 60, STREAM_CLIENT_CONNECT|STREAM_CLIENT_PERSISTENT, $ctx + ); + + if (!$fp) + exit("Failed to connect: $err $errstr" . PHP_EOL); + + $body['aps'] = array( + 'alert' => $message, + 'sound' => 'default' + ); + + $payload = json_encode($body); + + $msg = chr(0) . pack('n', 32) . pack('H*', $deviceToken) . pack('n', strlen($payload)) . $payload; + $result = fwrite($fp, $msg, strlen($msg)); + + if (!$result) + echo 'Message not delivered' . PHP_EOL; + else + echo 'Message successfully delivered' . PHP_EOL; + + fclose($fp); + } +} + +?> From 8ee18447c9d757ee572ddc0c21f63f47cf66e215 Mon Sep 17 00:00:00 2001 From: wess Date: Sat, 29 Jul 2023 09:41:20 -0400 Subject: [PATCH 02/39] Data structures and apns --- .../Adapters/Push/Apple/Rest/Config.php | 112 ++++++++++++++++++ .../Adapters/Push/Apple/Rest/Exception.php | 7 ++ .../Adapters/Push/Apple/Rest/Message.php | 41 +++++++ .../Adapters/Push/Apple/Rest/Request.php | 51 ++++++++ .../Adapters/Push/Apple/Rest/Response.php | 38 ++++++ .../Adapters/Push/{APNS.php => Apple/SSL.php} | 2 + .../Adapters/Push/Entities/Entity.php | 7 ++ .../Adapters/Push/Entities/Message.php | 43 +++++++ .../Adapters/Push/Entities/Provider.php | 35 ++++++ .../Adapters/Push/Entities/Subscriber.php | 31 +++++ .../Adapters/Push/Entities/Target.php | 35 ++++++ .../Adapters/Push/Entities/Topic.php | 29 +++++ 12 files changed, 431 insertions(+) create mode 100644 src/Utopia/Messaging/Adapters/Push/Apple/Rest/Config.php create mode 100644 src/Utopia/Messaging/Adapters/Push/Apple/Rest/Exception.php create mode 100644 src/Utopia/Messaging/Adapters/Push/Apple/Rest/Message.php create mode 100644 src/Utopia/Messaging/Adapters/Push/Apple/Rest/Request.php create mode 100644 src/Utopia/Messaging/Adapters/Push/Apple/Rest/Response.php rename src/Utopia/Messaging/Adapters/Push/{APNS.php => Apple/SSL.php} (94%) create mode 100644 src/Utopia/Messaging/Adapters/Push/Entities/Entity.php create mode 100644 src/Utopia/Messaging/Adapters/Push/Entities/Message.php create mode 100644 src/Utopia/Messaging/Adapters/Push/Entities/Provider.php create mode 100644 src/Utopia/Messaging/Adapters/Push/Entities/Subscriber.php create mode 100644 src/Utopia/Messaging/Adapters/Push/Entities/Target.php create mode 100644 src/Utopia/Messaging/Adapters/Push/Entities/Topic.php diff --git a/src/Utopia/Messaging/Adapters/Push/Apple/Rest/Config.php b/src/Utopia/Messaging/Adapters/Push/Apple/Rest/Config.php new file mode 100644 index 00000000..91f16fe0 --- /dev/null +++ b/src/Utopia/Messaging/Adapters/Push/Apple/Rest/Config.php @@ -0,0 +1,112 @@ + 'https://api.push.apple.com/3/device/', + 'sandbox' => 'https://api.development.push.apple.com/3/device/' + ]; + + /** + * @var string + */ + private $url; + + /** + * @var string + */ + private $keypath; + + /** + * @var string + */ + private $topic; + + /** + * @var string + */ + private $secretKey; + + /** + * @var string + */ + private $token; + + + /** + * Construct + * + * Construct a new APNS push configuration. + * + * @param string $env + * @param string $keypath + * @param string $secretKey + * @param string $topic + */ + public function __construct($env = Config::SANDBOX, $keypath, $secretKey, $topic) + { + $this->url = $this->endpoint[$env]; + $this->keypath = $keypath; + $this->secretKey = $secretKey; + $this->topic = $topic; + } + + /** + * Get Secret key + * + * @return string + */ + public function getSecretKey() + { + return $this->secretKey; + } + + /** + * Get Key path + * + * @return string + */ + public function getKeypath() + { + return $this->keypath; + } + + /** + * Get URL + * + * @return string + */ + public function getURL() + { + return $this->url; + } + + /** + * Get Topic + * + * @return string + */ + public function getTopic() + { + return $this->topic; + } + + /** + * Returns headers used for sending notification. + * + * @return associative array + * + * @todo: add support for various push types. + */ + public function getHeaders() { + return [ + 'apns-topic: ' . $this->getTopic(), + 'apns-push-type: ' . 'alert', + ]; + } +} \ No newline at end of file diff --git a/src/Utopia/Messaging/Adapters/Push/Apple/Rest/Exception.php b/src/Utopia/Messaging/Adapters/Push/Apple/Rest/Exception.php new file mode 100644 index 00000000..9eb94cbc --- /dev/null +++ b/src/Utopia/Messaging/Adapters/Push/Apple/Rest/Exception.php @@ -0,0 +1,7 @@ +title = $title; + $this->body = $body; + $this->sound = $sound; + } + + public function toJson() + { + return json_encode([ + 'aps' => [ + 'alert' => [ + 'title' => $this->title, + 'body' => $this->body + ], + 'sound' => $this->sound + ] + ]); + } +} \ No newline at end of file diff --git a/src/Utopia/Messaging/Adapters/Push/Apple/Rest/Request.php b/src/Utopia/Messaging/Adapters/Push/Apple/Rest/Request.php new file mode 100644 index 00000000..431f8f72 --- /dev/null +++ b/src/Utopia/Messaging/Adapters/Push/Apple/Rest/Request.php @@ -0,0 +1,51 @@ +config = $config; + } + + public function send(string $deviceToken, Message $message) + { + $url = $this->config->getURL() . $deviceToken; + + $ch = curl_init($url); + curl_setopt($ch, CURLOPT_HTTPHEADER, $this->config->getHeaders()); + curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($message->toJson())); + curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0); + + curl_setopt($ch, CURLOPT_SSLCERT, $this->config->getKeyPath()); + curl_setopt($ch, CURLOPT_SSLCERTPASSWD, $this->config->getSecretKey()); + + curl_setopt($ch, CURLOPT_FAILONERROR, true); + curl_exec($ch); + + $response = new Response(curl_getinfo($ch, CURLINFO_HTTP_CODE)); + + curl_close($ch); + + return [ + 'code' => $response->code, + 'response' => $response->description + ]; + } +} \ No newline at end of file diff --git a/src/Utopia/Messaging/Adapters/Push/Apple/Rest/Response.php b/src/Utopia/Messaging/Adapters/Push/Apple/Rest/Response.php new file mode 100644 index 00000000..df1ba709 --- /dev/null +++ b/src/Utopia/Messaging/Adapters/Push/Apple/Rest/Response.php @@ -0,0 +1,38 @@ + 'Invalid configuration.', + '200' => 'Success.', + '400' => 'Bad request.', + '403' => 'There was an error with the certificate or with the provider’s authentication token.', + '404' => 'The request contained an invalid :path value.', + '405' => 'The request used an invalid :method value. Only POST requests are supported.', + '410' => 'The device token is no longer active for the topic.', + '413' => 'The notification payload was too large.', + '429' => 'The server received too many requests for the same device token.', + '500' => 'Internal server error.', + '503' => 'The server is shutting down and unavailable.', + ]; + + /** + * @var string + */ + public $code; + + /** + * @var string + */ + public $description; + + public function __construct(string $statusCode) + { + $code = isset(self::$descriptions[$statusCode]) ? $statusCode : 0; + + $this->code = $code; + $this->description = self::$descriptions[$code]; + } +} \ No newline at end of file diff --git a/src/Utopia/Messaging/Adapters/Push/APNS.php b/src/Utopia/Messaging/Adapters/Push/Apple/SSL.php similarity index 94% rename from src/Utopia/Messaging/Adapters/Push/APNS.php rename to src/Utopia/Messaging/Adapters/Push/Apple/SSL.php index 0cce5fd8..21831f62 100644 --- a/src/Utopia/Messaging/Adapters/Push/APNS.php +++ b/src/Utopia/Messaging/Adapters/Push/Apple/SSL.php @@ -1,5 +1,7 @@ id = $id; + $this->providerId = $providerId; + $this->providerInternalId = $providerInternalId; + $this->data = $data; + $this->to = $to; + $this->deliveryTime = $deliveryTime; + $this->deliveryError = $deliveryError; + $this->deliveredTo = $deliveredTo; + $this->delivered = $delivered; + $this->search = $search; + } + +} \ No newline at end of file diff --git a/src/Utopia/Messaging/Adapters/Push/Entities/Provider.php b/src/Utopia/Messaging/Adapters/Push/Entities/Provider.php new file mode 100644 index 00000000..d85b604e --- /dev/null +++ b/src/Utopia/Messaging/Adapters/Push/Entities/Provider.php @@ -0,0 +1,35 @@ +id = $id; + $this->userId = $userId; + $this->name = $name; + $this->provider = $provider; + $this->type = $type; + $this->credentials = $credentials; + } + +} \ No newline at end of file diff --git a/src/Utopia/Messaging/Adapters/Push/Entities/Subscriber.php b/src/Utopia/Messaging/Adapters/Push/Entities/Subscriber.php new file mode 100644 index 00000000..d7594427 --- /dev/null +++ b/src/Utopia/Messaging/Adapters/Push/Entities/Subscriber.php @@ -0,0 +1,31 @@ +id = $id; + $this->userId = $userId; + $this->userInternalId = $userInternalId; + $this->targetId = $targetId; + $this->targetInternalId = $targetInternalId; + } + +} \ No newline at end of file diff --git a/src/Utopia/Messaging/Adapters/Push/Entities/Target.php b/src/Utopia/Messaging/Adapters/Push/Entities/Target.php new file mode 100644 index 00000000..2058440b --- /dev/null +++ b/src/Utopia/Messaging/Adapters/Push/Entities/Target.php @@ -0,0 +1,35 @@ +id = $id; + $this->userId = $userId; + $this->userInternalId = $userInternalId; + $this->providerId = $providerId; + $this->providerInternalId = $providerInternalId; + $this->providerType = $providerType; + $this->identifier = $identifier; + } + +} \ No newline at end of file diff --git a/src/Utopia/Messaging/Adapters/Push/Entities/Topic.php b/src/Utopia/Messaging/Adapters/Push/Entities/Topic.php new file mode 100644 index 00000000..b9759b8e --- /dev/null +++ b/src/Utopia/Messaging/Adapters/Push/Entities/Topic.php @@ -0,0 +1,29 @@ +id = $id; + $this->providerId = $providerId; + $this->providerInternalId = $providerInternalId; + $this->name = $name; + $this->description = $description; + } + +} \ No newline at end of file From 2e37e489e3f922d815baa37419bf61b18d616d61 Mon Sep 17 00:00:00 2001 From: wess Date: Mon, 31 Jul 2023 11:54:40 -0400 Subject: [PATCH 03/39] working on email adapter for service and impl --- src/Utopia/Messaging/Message.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Utopia/Messaging/Message.php b/src/Utopia/Messaging/Message.php index 3b5e21ae..c9533ad1 100644 --- a/src/Utopia/Messaging/Message.php +++ b/src/Utopia/Messaging/Message.php @@ -7,4 +7,5 @@ */ interface Message { + function getTo():string; } From 5ccda373cd0376f407c7114f810d6815dba15215 Mon Sep 17 00:00:00 2001 From: wess Date: Mon, 31 Jul 2023 12:10:29 -0400 Subject: [PATCH 04/39] Little reorg --- src/Utopia/Messaging/{Adapters/Push => }/Entities/Entity.php | 0 src/Utopia/Messaging/{Adapters/Push => }/Entities/Message.php | 0 src/Utopia/Messaging/{Adapters/Push => }/Entities/Provider.php | 0 .../Messaging/{Adapters/Push => }/Entities/Subscriber.php | 0 src/Utopia/Messaging/{Adapters/Push => }/Entities/Target.php | 0 src/Utopia/Messaging/{Adapters/Push => }/Entities/Topic.php | 0 src/Utopia/Messaging/Message.php | 3 ++- 7 files changed, 2 insertions(+), 1 deletion(-) rename src/Utopia/Messaging/{Adapters/Push => }/Entities/Entity.php (100%) rename src/Utopia/Messaging/{Adapters/Push => }/Entities/Message.php (100%) rename src/Utopia/Messaging/{Adapters/Push => }/Entities/Provider.php (100%) rename src/Utopia/Messaging/{Adapters/Push => }/Entities/Subscriber.php (100%) rename src/Utopia/Messaging/{Adapters/Push => }/Entities/Target.php (100%) rename src/Utopia/Messaging/{Adapters/Push => }/Entities/Topic.php (100%) diff --git a/src/Utopia/Messaging/Adapters/Push/Entities/Entity.php b/src/Utopia/Messaging/Entities/Entity.php similarity index 100% rename from src/Utopia/Messaging/Adapters/Push/Entities/Entity.php rename to src/Utopia/Messaging/Entities/Entity.php diff --git a/src/Utopia/Messaging/Adapters/Push/Entities/Message.php b/src/Utopia/Messaging/Entities/Message.php similarity index 100% rename from src/Utopia/Messaging/Adapters/Push/Entities/Message.php rename to src/Utopia/Messaging/Entities/Message.php diff --git a/src/Utopia/Messaging/Adapters/Push/Entities/Provider.php b/src/Utopia/Messaging/Entities/Provider.php similarity index 100% rename from src/Utopia/Messaging/Adapters/Push/Entities/Provider.php rename to src/Utopia/Messaging/Entities/Provider.php diff --git a/src/Utopia/Messaging/Adapters/Push/Entities/Subscriber.php b/src/Utopia/Messaging/Entities/Subscriber.php similarity index 100% rename from src/Utopia/Messaging/Adapters/Push/Entities/Subscriber.php rename to src/Utopia/Messaging/Entities/Subscriber.php diff --git a/src/Utopia/Messaging/Adapters/Push/Entities/Target.php b/src/Utopia/Messaging/Entities/Target.php similarity index 100% rename from src/Utopia/Messaging/Adapters/Push/Entities/Target.php rename to src/Utopia/Messaging/Entities/Target.php diff --git a/src/Utopia/Messaging/Adapters/Push/Entities/Topic.php b/src/Utopia/Messaging/Entities/Topic.php similarity index 100% rename from src/Utopia/Messaging/Adapters/Push/Entities/Topic.php rename to src/Utopia/Messaging/Entities/Topic.php diff --git a/src/Utopia/Messaging/Message.php b/src/Utopia/Messaging/Message.php index c9533ad1..0b70a2ce 100644 --- a/src/Utopia/Messaging/Message.php +++ b/src/Utopia/Messaging/Message.php @@ -7,5 +7,6 @@ */ interface Message { - function getTo():string; + function getTo():array; + function getFrom():?string; } From e9c792a82e722447573f571d415e3254868bfc28 Mon Sep 17 00:00:00 2001 From: wess Date: Wed, 2 Aug 2023 14:30:25 -0400 Subject: [PATCH 05/39] Sendgrid test and some key clean up --- .gitignore | 4 +- composer.json | 4 +- composer.lock | 49 ++++++++++--------- docker-compose.yml | 2 + phpunit.xml | 2 +- .../Messaging/Adapters/Email/Mailgun.php | 6 ++- .../Messaging/Adapters/Email/Sendgrid.php | 4 +- src/Utopia/Messaging/Entities/Target.php | 1 - tests/e2e/Email/MailgunTest.php | 40 +++++++++++++++ tests/e2e/Email/SendgridTest.php | 34 +++++++++++++ 10 files changed, 113 insertions(+), 33 deletions(-) create mode 100644 tests/e2e/Email/MailgunTest.php create mode 100644 tests/e2e/Email/SendgridTest.php diff --git a/.gitignore b/.gitignore index 7539ebce..5176c240 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ .idea vendor .phpunit.result.cache -Makefile \ No newline at end of file +Makefile +.envrc +.env \ No newline at end of file diff --git a/composer.json b/composer.json index 0e9de463..7dfe178e 100644 --- a/composer.json +++ b/composer.json @@ -26,8 +26,8 @@ "ext-curl": "*" }, "require-dev": { - "phpunit/phpunit": "9.5.*", - "phpmailer/phpmailer": "6.6.*", + "phpunit/phpunit": "^9.6", + "phpmailer/phpmailer": "^6.8", "laravel/pint": "^1.2" }, "config": { diff --git a/composer.lock b/composer.lock index c2118454..9f6b978b 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "1ca9da311c804e40032e90c09ad04d76", + "content-hash": "5ecbd865cbd7f14e7819fb79643573be", "packages": [], "packages-dev": [ { @@ -371,16 +371,16 @@ }, { "name": "phpmailer/phpmailer", - "version": "v6.6.4", + "version": "v6.8.0", "source": { "type": "git", "url": "https://github.com/PHPMailer/PHPMailer.git", - "reference": "a94fdebaea6bd17f51be0c2373ab80d3d681269b" + "reference": "df16b615e371d81fb79e506277faea67a1be18f1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/a94fdebaea6bd17f51be0c2373ab80d3d681269b", - "reference": "a94fdebaea6bd17f51be0c2373ab80d3d681269b", + "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/df16b615e371d81fb79e506277faea67a1be18f1", + "reference": "df16b615e371d81fb79e506277faea67a1be18f1", "shasum": "" }, "require": { @@ -390,22 +390,24 @@ "php": ">=5.5.0" }, "require-dev": { - "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0", - "doctrine/annotations": "^1.2", + "dealerdirect/phpcodesniffer-composer-installer": "^0.7.2", + "doctrine/annotations": "^1.2.6 || ^1.13.3", "php-parallel-lint/php-console-highlighter": "^1.0.0", "php-parallel-lint/php-parallel-lint": "^1.3.2", "phpcompatibility/php-compatibility": "^9.3.5", "roave/security-advisories": "dev-latest", - "squizlabs/php_codesniffer": "^3.6.2", - "yoast/phpunit-polyfills": "^1.0.0" + "squizlabs/php_codesniffer": "^3.7.1", + "yoast/phpunit-polyfills": "^1.0.4" }, "suggest": { "ext-mbstring": "Needed to send email in multibyte encoding charset or decode encoded addresses", + "ext-openssl": "Needed for secure SMTP sending and DKIM signing", + "greew/oauth2-azure-provider": "Needed for Microsoft Azure XOAUTH2 authentication", "hayageek/oauth2-yahoo": "Needed for Yahoo XOAUTH2 authentication", "league/oauth2-google": "Needed for Google XOAUTH2 authentication", "psr/log": "For optional PSR-3 debug logging", - "stevenmaguire/oauth2-microsoft": "Needed for Microsoft XOAUTH2 authentication", - "symfony/polyfill-mbstring": "To support UTF-8 if the Mbstring PHP extension is not enabled (^1.2)" + "symfony/polyfill-mbstring": "To support UTF-8 if the Mbstring PHP extension is not enabled (^1.2)", + "thenetworg/oauth2-azure": "Needed for Microsoft XOAUTH2 authentication" }, "type": "library", "autoload": { @@ -437,7 +439,7 @@ "description": "PHPMailer is a full-featured email creation and transfer class for PHP", "support": { "issues": "https://github.com/PHPMailer/PHPMailer/issues", - "source": "https://github.com/PHPMailer/PHPMailer/tree/v6.6.4" + "source": "https://github.com/PHPMailer/PHPMailer/tree/v6.8.0" }, "funding": [ { @@ -445,7 +447,7 @@ "type": "github" } ], - "time": "2022-08-22T09:22:00+00:00" + "time": "2023-03-06T14:43:22+00:00" }, { "name": "phpunit/php-code-coverage", @@ -767,20 +769,20 @@ }, { "name": "phpunit/phpunit", - "version": "9.5.25", + "version": "9.6.10", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "3e6f90ca7e3d02025b1d147bd8d4a89fd4ca8a1d" + "reference": "a6d351645c3fe5a30f5e86be6577d946af65a328" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/3e6f90ca7e3d02025b1d147bd8d4a89fd4ca8a1d", - "reference": "3e6f90ca7e3d02025b1d147bd8d4a89fd4ca8a1d", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/a6d351645c3fe5a30f5e86be6577d946af65a328", + "reference": "a6d351645c3fe5a30f5e86be6577d946af65a328", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.3.1", + "doctrine/instantiator": "^1.3.1 || ^2", "ext-dom": "*", "ext-json": "*", "ext-libxml": "*", @@ -809,8 +811,8 @@ "sebastian/version": "^3.0.2" }, "suggest": { - "ext-soap": "*", - "ext-xdebug": "*" + "ext-soap": "To be able to generate mocks based on WSDL files", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" }, "bin": [ "phpunit" @@ -818,7 +820,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "9.5-dev" + "dev-master": "9.6-dev" } }, "autoload": { @@ -849,7 +851,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.25" + "security": "https://github.com/sebastianbergmann/phpunit/security/policy", + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.10" }, "funding": [ { @@ -865,7 +868,7 @@ "type": "tidelift" } ], - "time": "2022-09-25T03:44:45+00:00" + "time": "2023-07-10T04:04:23+00:00" }, { "name": "sebastian/cli-parser", diff --git a/docker-compose.yml b/docker-compose.yml index 96054433..e0a8130f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,6 +8,8 @@ services: - ./src:/usr/local/src/src - ./tests:/usr/local/src/tests - ./phpunit.xml:/usr/local/src/phpunit.xml + env_file: + - .env maildev: image: appwrite/mailcatcher:1.0.0 diff --git a/phpunit.xml b/phpunit.xml index 8db6586a..53cd4abd 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -6,7 +6,7 @@ convertNoticesToExceptions="true" convertWarningsToExceptions="true" processIsolation="false" - stopOnFailure="false" + stopOnFailure="true" > diff --git a/src/Utopia/Messaging/Adapters/Email/Mailgun.php b/src/Utopia/Messaging/Adapters/Email/Mailgun.php index ba64c906..148cb174 100644 --- a/src/Utopia/Messaging/Adapters/Email/Mailgun.php +++ b/src/Utopia/Messaging/Adapters/Email/Mailgun.php @@ -34,19 +34,21 @@ public function getMaxMessagesPerRequest(): int */ protected function process(Email $message): string { - return $this->request( + $response = $this->request( method: 'POST', url: "https://api.mailgun.net/v3/{$this->domain}/messages", headers: [ 'Authorization: Basic '.base64_encode('api:'.$this->apiKey), ], body: \http_build_query([ - 'from' => $message->getFrom(), 'to' => \implode(',', $message->getTo()), + 'from' => $message->getFrom(), 'subject' => $message->getSubject(), 'text' => $message->isHtml() ? null : $message->getContent(), 'html' => $message->isHtml() ? $message->getContent() : null, ]), ); + + return $response; } } diff --git a/src/Utopia/Messaging/Adapters/Email/Sendgrid.php b/src/Utopia/Messaging/Adapters/Email/Sendgrid.php index 17745c48..a805e6c6 100644 --- a/src/Utopia/Messaging/Adapters/Email/Sendgrid.php +++ b/src/Utopia/Messaging/Adapters/Email/Sendgrid.php @@ -7,9 +7,7 @@ class Sendgrid extends EmailAdapter { - public function __construct( - private string $apiKey, - ) { + public function __construct(private string $apiKey) { } public function getName(): string diff --git a/src/Utopia/Messaging/Entities/Target.php b/src/Utopia/Messaging/Entities/Target.php index 2058440b..b0d37a35 100644 --- a/src/Utopia/Messaging/Entities/Target.php +++ b/src/Utopia/Messaging/Entities/Target.php @@ -2,7 +2,6 @@ namespace Utopia\Messaging\Adapters\Push\Entities; -use DateTime; use Utopia\Messaging\Adapters\Push\Entities\Entity; class Target extends Entity { diff --git a/tests/e2e/Email/MailgunTest.php b/tests/e2e/Email/MailgunTest.php new file mode 100644 index 00000000..0f55ee15 --- /dev/null +++ b/tests/e2e/Email/MailgunTest.php @@ -0,0 +1,40 @@ +send($message); + + $this->assertEquals(true, true); + } +} diff --git a/tests/e2e/Email/SendgridTest.php b/tests/e2e/Email/SendgridTest.php new file mode 100644 index 00000000..0786a1eb --- /dev/null +++ b/tests/e2e/Email/SendgridTest.php @@ -0,0 +1,34 @@ +send($message); + + $this->assertEquals(true, true); + } +} From cb8f99efa6244dd77809dc3607226999f401404f Mon Sep 17 00:00:00 2001 From: wess Date: Thu, 3 Aug 2023 15:48:09 -0400 Subject: [PATCH 06/39] Removes unneeded classes for apns Creates APNS Adapter Creates tests for Apple Push --- .gitignore | 3 +- src/Utopia/Messaging/Adapters/Push/APNS.php | 95 +++++++++++++++ .../Adapters/Push/Apple/Rest/Config.php | 112 ------------------ .../Adapters/Push/Apple/Rest/Exception.php | 7 -- .../Adapters/Push/Apple/Rest/Message.php | 41 ------- .../Adapters/Push/Apple/Rest/Request.php | 51 -------- .../Adapters/Push/Apple/Rest/Response.php | 38 ------ .../Messaging/Adapters/Push/Apple/SSL.php | 35 ------ src/Utopia/Messaging/Apple/Notification.php | 51 -------- src/Utopia/Messaging/Messages/Push.php | 5 + tests/e2e/Push/APNSTest.php | 37 ++++++ 11 files changed, 139 insertions(+), 336 deletions(-) create mode 100644 src/Utopia/Messaging/Adapters/Push/APNS.php delete mode 100644 src/Utopia/Messaging/Adapters/Push/Apple/Rest/Config.php delete mode 100644 src/Utopia/Messaging/Adapters/Push/Apple/Rest/Exception.php delete mode 100644 src/Utopia/Messaging/Adapters/Push/Apple/Rest/Message.php delete mode 100644 src/Utopia/Messaging/Adapters/Push/Apple/Rest/Request.php delete mode 100644 src/Utopia/Messaging/Adapters/Push/Apple/Rest/Response.php delete mode 100644 src/Utopia/Messaging/Adapters/Push/Apple/SSL.php delete mode 100644 src/Utopia/Messaging/Apple/Notification.php create mode 100644 tests/e2e/Push/APNSTest.php diff --git a/.gitignore b/.gitignore index 5176c240..03bf4c75 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ vendor .phpunit.result.cache Makefile .envrc -.env \ No newline at end of file +.env +*.p8 \ No newline at end of file diff --git a/src/Utopia/Messaging/Adapters/Push/APNS.php b/src/Utopia/Messaging/Adapters/Push/APNS.php new file mode 100644 index 00000000..b5bd8a03 --- /dev/null +++ b/src/Utopia/Messaging/Adapters/Push/APNS.php @@ -0,0 +1,95 @@ +authKey = $authKey; + $this->authKeyId = $authKeyId; + $this->teamId = $teamId; + $this->bundleId = $bundleId; + $this->endpoint = $endpoint; + } + + public function getName(): string + { + return 'APNS'; + } + + + public function getMaxMessagesPerRequest(): int + { + return 1000; + } + + public function process(Push $message): string + { + $headers = [ + 'authorization: bearer ' . $this->generateJwt(), + 'apns-topic: ' . $this->bundleId, + ]; + + $payload = json_encode([ + 'aps' => [ + 'alert' => [ + 'title' => $message->getTitle(), + 'body' => $message->getBody(), + ], + 'badge' => $message->getBadge(), + 'sound' => $message->getSound(), + 'data' => $message->getData(), + ], + ]); + + // Assuming the 'to' array contains device tokens for the push notification recipients. + foreach ($message->getTo() as $to) { + $url = $this->endpoint . '/3/device/' . $to; + $response = $this->request('POST', $url, $headers, $payload); + + // You might want to handle each response here, for instance, logging failures + } + + // This example simply returns the last response, adjust as needed + return $response; + } + + private function generateJwt(): string + { + $header = json_encode(['alg' => 'ES256', 'kid' => $this->authKeyId]); + $claims = json_encode([ + 'iss' => $this->teamId, + 'iat' => time(), + ]); + + $base64UrlHeader = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($header)); + $base64UrlClaims = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($claims)); + + $privateKeyResource = openssl_pkey_get_private(file_get_contents($this->authKey)); + if (!$privateKeyResource) { + throw new \Exception('Invalid private key'); + } + + $signature = ''; + $success = openssl_sign("$base64UrlHeader.$base64UrlClaims", $signature, $privateKeyResource, OPENSSL_ALGO_SHA256); + + if (!$success) { + throw new \Exception('Failed to sign JWT'); + } + + $base64UrlSignature = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($signature)); + + return "$base64UrlHeader.$base64UrlClaims.$base64UrlSignature"; + } + +} diff --git a/src/Utopia/Messaging/Adapters/Push/Apple/Rest/Config.php b/src/Utopia/Messaging/Adapters/Push/Apple/Rest/Config.php deleted file mode 100644 index 91f16fe0..00000000 --- a/src/Utopia/Messaging/Adapters/Push/Apple/Rest/Config.php +++ /dev/null @@ -1,112 +0,0 @@ - 'https://api.push.apple.com/3/device/', - 'sandbox' => 'https://api.development.push.apple.com/3/device/' - ]; - - /** - * @var string - */ - private $url; - - /** - * @var string - */ - private $keypath; - - /** - * @var string - */ - private $topic; - - /** - * @var string - */ - private $secretKey; - - /** - * @var string - */ - private $token; - - - /** - * Construct - * - * Construct a new APNS push configuration. - * - * @param string $env - * @param string $keypath - * @param string $secretKey - * @param string $topic - */ - public function __construct($env = Config::SANDBOX, $keypath, $secretKey, $topic) - { - $this->url = $this->endpoint[$env]; - $this->keypath = $keypath; - $this->secretKey = $secretKey; - $this->topic = $topic; - } - - /** - * Get Secret key - * - * @return string - */ - public function getSecretKey() - { - return $this->secretKey; - } - - /** - * Get Key path - * - * @return string - */ - public function getKeypath() - { - return $this->keypath; - } - - /** - * Get URL - * - * @return string - */ - public function getURL() - { - return $this->url; - } - - /** - * Get Topic - * - * @return string - */ - public function getTopic() - { - return $this->topic; - } - - /** - * Returns headers used for sending notification. - * - * @return associative array - * - * @todo: add support for various push types. - */ - public function getHeaders() { - return [ - 'apns-topic: ' . $this->getTopic(), - 'apns-push-type: ' . 'alert', - ]; - } -} \ No newline at end of file diff --git a/src/Utopia/Messaging/Adapters/Push/Apple/Rest/Exception.php b/src/Utopia/Messaging/Adapters/Push/Apple/Rest/Exception.php deleted file mode 100644 index 9eb94cbc..00000000 --- a/src/Utopia/Messaging/Adapters/Push/Apple/Rest/Exception.php +++ /dev/null @@ -1,7 +0,0 @@ -title = $title; - $this->body = $body; - $this->sound = $sound; - } - - public function toJson() - { - return json_encode([ - 'aps' => [ - 'alert' => [ - 'title' => $this->title, - 'body' => $this->body - ], - 'sound' => $this->sound - ] - ]); - } -} \ No newline at end of file diff --git a/src/Utopia/Messaging/Adapters/Push/Apple/Rest/Request.php b/src/Utopia/Messaging/Adapters/Push/Apple/Rest/Request.php deleted file mode 100644 index 431f8f72..00000000 --- a/src/Utopia/Messaging/Adapters/Push/Apple/Rest/Request.php +++ /dev/null @@ -1,51 +0,0 @@ -config = $config; - } - - public function send(string $deviceToken, Message $message) - { - $url = $this->config->getURL() . $deviceToken; - - $ch = curl_init($url); - curl_setopt($ch, CURLOPT_HTTPHEADER, $this->config->getHeaders()); - curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($message->toJson())); - curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0); - - curl_setopt($ch, CURLOPT_SSLCERT, $this->config->getKeyPath()); - curl_setopt($ch, CURLOPT_SSLCERTPASSWD, $this->config->getSecretKey()); - - curl_setopt($ch, CURLOPT_FAILONERROR, true); - curl_exec($ch); - - $response = new Response(curl_getinfo($ch, CURLINFO_HTTP_CODE)); - - curl_close($ch); - - return [ - 'code' => $response->code, - 'response' => $response->description - ]; - } -} \ No newline at end of file diff --git a/src/Utopia/Messaging/Adapters/Push/Apple/Rest/Response.php b/src/Utopia/Messaging/Adapters/Push/Apple/Rest/Response.php deleted file mode 100644 index df1ba709..00000000 --- a/src/Utopia/Messaging/Adapters/Push/Apple/Rest/Response.php +++ /dev/null @@ -1,38 +0,0 @@ - 'Invalid configuration.', - '200' => 'Success.', - '400' => 'Bad request.', - '403' => 'There was an error with the certificate or with the provider’s authentication token.', - '404' => 'The request contained an invalid :path value.', - '405' => 'The request used an invalid :method value. Only POST requests are supported.', - '410' => 'The device token is no longer active for the topic.', - '413' => 'The notification payload was too large.', - '429' => 'The server received too many requests for the same device token.', - '500' => 'Internal server error.', - '503' => 'The server is shutting down and unavailable.', - ]; - - /** - * @var string - */ - public $code; - - /** - * @var string - */ - public $description; - - public function __construct(string $statusCode) - { - $code = isset(self::$descriptions[$statusCode]) ? $statusCode : 0; - - $this->code = $code; - $this->description = self::$descriptions[$code]; - } -} \ No newline at end of file diff --git a/src/Utopia/Messaging/Adapters/Push/Apple/SSL.php b/src/Utopia/Messaging/Adapters/Push/Apple/SSL.php deleted file mode 100644 index 21831f62..00000000 --- a/src/Utopia/Messaging/Adapters/Push/Apple/SSL.php +++ /dev/null @@ -1,35 +0,0 @@ -client = new ApplePushNotification($certificatePath, $passphrase, $ssl); - } - - public function getName(): string - { - return 'applePush'; - } - - public function process(Push $message): string - { - try { - $this->client->send($message->getTo(), $message->getBody()); - - return true; - } catch (\Exception $e) { - throw new \Exception($e->getMessage(), 500); - } - } -} - -?> diff --git a/src/Utopia/Messaging/Apple/Notification.php b/src/Utopia/Messaging/Apple/Notification.php deleted file mode 100644 index 7821f7f8..00000000 --- a/src/Utopia/Messaging/Apple/Notification.php +++ /dev/null @@ -1,51 +0,0 @@ -certificatePath = $certificatePath; - $this->passphrase = $passphrase; - $this->ssl = $ssl; - } - - public function send($deviceToken, $message) - { - $ctx = stream_context_create(); - stream_context_set_option($ctx, 'ssl', 'local_cert', $this->certificatePath); - stream_context_set_option($ctx, 'ssl', 'passphrase', $this->passphrase); - - $fp = stream_socket_client( - $this->ssl, $err, - $errstr, 60, STREAM_CLIENT_CONNECT|STREAM_CLIENT_PERSISTENT, $ctx - ); - - if (!$fp) - exit("Failed to connect: $err $errstr" . PHP_EOL); - - $body['aps'] = array( - 'alert' => $message, - 'sound' => 'default' - ); - - $payload = json_encode($body); - - $msg = chr(0) . pack('n', 32) . pack('H*', $deviceToken) . pack('n', strlen($payload)) . $payload; - $result = fwrite($fp, $msg, strlen($msg)); - - if (!$result) - echo 'Message not delivered' . PHP_EOL; - else - echo 'Message successfully delivered' . PHP_EOL; - - fclose($fp); - } -} - -?> diff --git a/src/Utopia/Messaging/Messages/Push.php b/src/Utopia/Messaging/Messages/Push.php index d4a4333b..12e1d7c3 100644 --- a/src/Utopia/Messaging/Messages/Push.php +++ b/src/Utopia/Messaging/Messages/Push.php @@ -40,6 +40,11 @@ public function getTo(): array return $this->to; } + public function getFrom(): ?string + { + return null; + } + /** * @return string */ diff --git a/tests/e2e/Push/APNSTest.php b/tests/e2e/Push/APNSTest.php new file mode 100644 index 00000000..39fe3dd6 --- /dev/null +++ b/tests/e2e/Push/APNSTest.php @@ -0,0 +1,37 @@ +send($message); + + $this->assertEquals('', $response); + } +} From f0c5cbb762ae28e072a7574affa44798e1b34415 Mon Sep 17 00:00:00 2001 From: wess Date: Fri, 4 Aug 2023 13:45:25 -0400 Subject: [PATCH 07/39] Removes TwilioNotify as Twilio is ending it this year Adds FCM and FCM tests Twilio update and tests --- .../Messaging/Adapters/SMS/TwilioNotify.php | 53 ------------------- tests/e2e/Push/FCMTest.php | 33 ++++++++++++ tests/e2e/SMS/TwilioTest.php | 35 ++++++++++++ 3 files changed, 68 insertions(+), 53 deletions(-) delete mode 100644 src/Utopia/Messaging/Adapters/SMS/TwilioNotify.php create mode 100644 tests/e2e/Push/FCMTest.php create mode 100644 tests/e2e/SMS/TwilioTest.php diff --git a/src/Utopia/Messaging/Adapters/SMS/TwilioNotify.php b/src/Utopia/Messaging/Adapters/SMS/TwilioNotify.php deleted file mode 100644 index a977ed79..00000000 --- a/src/Utopia/Messaging/Adapters/SMS/TwilioNotify.php +++ /dev/null @@ -1,53 +0,0 @@ -request( - method: 'POST', - url: "https://notify.twilio.com/v1/Services/{$this->serviceSid}/Notifications", - headers: [ - 'Authorization: Basic '.base64_encode("{$this->accountSid}:{$this->authToken}"), - ], - body: \http_build_query([ - 'Body' => $message->getContent(), - 'ToBinding' => \json_encode(\array_map( - fn ($to) => ['binding_type' => 'sms', 'address' => $to], - $message->getTo() - )), - ]), - ); - } -} diff --git a/tests/e2e/Push/FCMTest.php b/tests/e2e/Push/FCMTest.php new file mode 100644 index 00000000..cd10658c --- /dev/null +++ b/tests/e2e/Push/FCMTest.php @@ -0,0 +1,33 @@ +send($message); + + $this->assertNotEmpty($response); + } +} diff --git a/tests/e2e/SMS/TwilioTest.php b/tests/e2e/SMS/TwilioTest.php new file mode 100644 index 00000000..21e1f87e --- /dev/null +++ b/tests/e2e/SMS/TwilioTest.php @@ -0,0 +1,35 @@ +send($message); + + $smsRequest = $this->getLastRequest(); + + $this->assertEquals('http://request-catcher:5000/mock-sms', $smsRequest['url']); + $this->assertEquals('Appwrite Mock Message Sender', $smsRequest['headers']['User-Agent']); + $this->assertEquals('username', $smsRequest['headers']['X-Username']); + $this->assertEquals('password', $smsRequest['headers']['X-Key']); + $this->assertEquals('POST', $smsRequest['method']); + $this->assertEquals('+987654321', $smsRequest['data']['from']); + $this->assertEquals('+123456789', $smsRequest['data']['to']); + } +} From 6472702c74a4e03c9b63ec572c6abcf80011571f Mon Sep 17 00:00:00 2001 From: wess Date: Fri, 4 Aug 2023 14:58:28 -0400 Subject: [PATCH 08/39] Twillio test Stubbed in, but on hold, other tests because of different requirements/issues --- tests/e2e/SMS/Msg91Test.php | 27 +++++++++++++++++++++++++++ tests/e2e/SMS/TelesignTest.php | 32 ++++++++++++++++++++++++++++++++ tests/e2e/SMS/TelnyxTest.php | 29 +++++++++++++++++++++++++++++ tests/e2e/SMS/TwilioTest.php | 14 +++----------- tests/e2e/SMS/vonage.php | 29 +++++++++++++++++++++++++++++ 5 files changed, 120 insertions(+), 11 deletions(-) create mode 100644 tests/e2e/SMS/Msg91Test.php create mode 100644 tests/e2e/SMS/TelesignTest.php create mode 100644 tests/e2e/SMS/TelnyxTest.php create mode 100644 tests/e2e/SMS/vonage.php diff --git a/tests/e2e/SMS/Msg91Test.php b/tests/e2e/SMS/Msg91Test.php new file mode 100644 index 00000000..3183d764 --- /dev/null +++ b/tests/e2e/SMS/Msg91Test.php @@ -0,0 +1,27 @@ +send($message), true); + + $this->assertEquals('success', $result["type"]); + } +} diff --git a/tests/e2e/SMS/TelesignTest.php b/tests/e2e/SMS/TelesignTest.php new file mode 100644 index 00000000..7cf276f9 --- /dev/null +++ b/tests/e2e/SMS/TelesignTest.php @@ -0,0 +1,32 @@ +send($message), true); + + // $this->assertEquals('success', $result["type"]); + + $this->markTestSkipped('Telesign requires sales calls and such to setup an account'); + } +} diff --git a/tests/e2e/SMS/TelnyxTest.php b/tests/e2e/SMS/TelnyxTest.php new file mode 100644 index 00000000..dc711498 --- /dev/null +++ b/tests/e2e/SMS/TelnyxTest.php @@ -0,0 +1,29 @@ +send($message), true); + + // $this->assertEquals('success', $result["type"]); + + $this->markTestSkipped('Telnyx had no testing numbers available at this time.'); + } +} diff --git a/tests/e2e/SMS/TwilioTest.php b/tests/e2e/SMS/TwilioTest.php index 21e1f87e..3c50ae4e 100644 --- a/tests/e2e/SMS/TwilioTest.php +++ b/tests/e2e/SMS/TwilioTest.php @@ -12,7 +12,7 @@ class TwilioTest extends Base */ public function testSendSMS() { - $sender = new Twilio('AC902ede6fda93fb923cc2c3128a2b79bd', 'cfc931ba5b7c1a3878ef5696938c8afc'); + $sender = new Twilio(getenv('TWILIO_ACCOUNT_SID'), getenv('TWILIO_AUTH_TOKEN')); $message = new SMS( to: ['+18034041123'], @@ -20,16 +20,8 @@ public function testSendSMS() from: '+15005550006' ); - $sender->send($message); + $result = $sender->send($message); - $smsRequest = $this->getLastRequest(); - - $this->assertEquals('http://request-catcher:5000/mock-sms', $smsRequest['url']); - $this->assertEquals('Appwrite Mock Message Sender', $smsRequest['headers']['User-Agent']); - $this->assertEquals('username', $smsRequest['headers']['X-Username']); - $this->assertEquals('password', $smsRequest['headers']['X-Key']); - $this->assertEquals('POST', $smsRequest['method']); - $this->assertEquals('+987654321', $smsRequest['data']['from']); - $this->assertEquals('+123456789', $smsRequest['data']['to']); + $this->assertNotEmpty($result); } } diff --git a/tests/e2e/SMS/vonage.php b/tests/e2e/SMS/vonage.php new file mode 100644 index 00000000..dc175df9 --- /dev/null +++ b/tests/e2e/SMS/vonage.php @@ -0,0 +1,29 @@ +send($message), true); + + // $this->assertEquals('success', $result["type"]); + + $this->markTestSkipped('Requires a business account, and to contact them first before getting access.'); + } +} From 40eada32e6357b5bd0495735f35ed8bd9f8b21da Mon Sep 17 00:00:00 2001 From: wess Date: Mon, 7 Aug 2023 10:33:29 -0400 Subject: [PATCH 09/39] Removes, decided, unneeded entities Cleans up some docs Linted up the formatting --- .../Messaging/Adapters/Email/Mailgun.php | 10 +++ .../Messaging/Adapters/Email/Sendgrid.php | 26 +++++- src/Utopia/Messaging/Adapters/Push/APNS.php | 82 ++++++++++++------- src/Utopia/Messaging/Adapters/Push/FCM.php | 10 +++ src/Utopia/Messaging/Entities/Entity.php | 7 -- src/Utopia/Messaging/Entities/Message.php | 43 ---------- src/Utopia/Messaging/Entities/Provider.php | 35 -------- src/Utopia/Messaging/Entities/Subscriber.php | 31 ------- src/Utopia/Messaging/Entities/Target.php | 34 -------- src/Utopia/Messaging/Entities/Topic.php | 29 ------- src/Utopia/Messaging/Message.php | 5 +- src/Utopia/Messaging/Messages/Push.php | 2 +- tests/e2e/Email/MailgunTest.php | 7 +- tests/e2e/Push/APNSTest.php | 2 +- tests/e2e/Push/FCMTest.php | 22 ++--- tests/e2e/SMS/Msg91Test.php | 2 +- 16 files changed, 119 insertions(+), 228 deletions(-) delete mode 100644 src/Utopia/Messaging/Entities/Entity.php delete mode 100644 src/Utopia/Messaging/Entities/Message.php delete mode 100644 src/Utopia/Messaging/Entities/Provider.php delete mode 100644 src/Utopia/Messaging/Entities/Subscriber.php delete mode 100644 src/Utopia/Messaging/Entities/Target.php delete mode 100644 src/Utopia/Messaging/Entities/Topic.php diff --git a/src/Utopia/Messaging/Adapters/Email/Mailgun.php b/src/Utopia/Messaging/Adapters/Email/Mailgun.php index 148cb174..5fbdcb01 100644 --- a/src/Utopia/Messaging/Adapters/Email/Mailgun.php +++ b/src/Utopia/Messaging/Adapters/Email/Mailgun.php @@ -17,11 +17,21 @@ public function __construct( ) { } + /** + * Get adapter name. + * + * @return string + */ public function getName(): string { return 'Mailgun'; } + /** + * Get adapter description. + * + * @return int + */ public function getMaxMessagesPerRequest(): int { return 1000; diff --git a/src/Utopia/Messaging/Adapters/Email/Sendgrid.php b/src/Utopia/Messaging/Adapters/Email/Sendgrid.php index a805e6c6..28dc661b 100644 --- a/src/Utopia/Messaging/Adapters/Email/Sendgrid.php +++ b/src/Utopia/Messaging/Adapters/Email/Sendgrid.php @@ -2,24 +2,48 @@ namespace Utopia\Messaging\Adapters\Email; +use Exception; use Utopia\Messaging\Adapters\Email as EmailAdapter; use Utopia\Messaging\Messages\Email; class Sendgrid extends EmailAdapter { - public function __construct(private string $apiKey) { + /** + * @param string $apiKey Your Sendgrid API key to authenticate with the API. + * @return void + */ + public function __construct(private string $apiKey) + { } + /** + * Get adapter name. + * + * @return string + */ public function getName(): string { return 'Sendgrid'; } + /** + * Get max messages per request. + * + * @return int + */ public function getMaxMessagesPerRequest(): int { return 1000; } + /** + * {@inheritdoc} + * + * @param Email $message + * @return string + * + * @throws Exception + */ protected function process(Email $message): string { return $this->request( diff --git a/src/Utopia/Messaging/Adapters/Push/APNS.php b/src/Utopia/Messaging/Adapters/Push/APNS.php index b5bd8a03..8c5b2597 100644 --- a/src/Utopia/Messaging/Adapters/Push/APNS.php +++ b/src/Utopia/Messaging/Adapters/Push/APNS.php @@ -2,42 +2,62 @@ namespace Utopia\Messaging\Adapters\Push; +use Exception; use Utopia\Messaging\Adapters\Push as PushAdapter; use Utopia\Messaging\Messages\Push; class APNS extends PushAdapter { - private $authKey; - private $authKeyId; - private $teamId; - private $bundleId; - private $endpoint; - - public function __construct(string $authKey, string $authKeyId, string $teamId, string $bundleId, string $endpoint) - { - $this->authKey = $authKey; - $this->authKeyId = $authKeyId; - $this->teamId = $teamId; - $this->bundleId = $bundleId; - $this->endpoint = $endpoint; + /** + * @param string $authKey + * @param string $authKeyId + * @param string $teamId + * @param string $bundleId + * @param string $endpoint + * @return void + */ + public function __construct( + private string $authKey, + private string $authKeyId, + private string $teamId, + private string $bundleId, + private string $endpoint + ) { } + /** + * Get adapter name. + * + * @return string + */ public function getName(): string { return 'APNS'; } - + /** + * Get max messages per request. + * + * @return int + */ public function getMaxMessagesPerRequest(): int { return 1000; } + /** + * {@inheritdoc} + * + * @param Push $message + * @return string + * + * @throws Exception + */ public function process(Push $message): string { $headers = [ - 'authorization: bearer ' . $this->generateJwt(), - 'apns-topic: ' . $this->bundleId, + 'authorization: bearer '.$this->generateJwt(), + 'apns-topic: '.$this->bundleId, ]; $payload = json_encode([ @@ -54,16 +74,23 @@ public function process(Push $message): string // Assuming the 'to' array contains device tokens for the push notification recipients. foreach ($message->getTo() as $to) { - $url = $this->endpoint . '/3/device/' . $to; + $url = $this->endpoint.'/3/device/'.$to; $response = $this->request('POST', $url, $headers, $payload); - + // You might want to handle each response here, for instance, logging failures } // This example simply returns the last response, adjust as needed return $response; } - + + /** + * Generate JWT. + * + * @return string + * + * @throws Exception + */ private function generateJwt(): string { $header = json_encode(['alg' => 'ES256', 'kid' => $this->authKeyId]); @@ -71,25 +98,24 @@ private function generateJwt(): string 'iss' => $this->teamId, 'iat' => time(), ]); - + $base64UrlHeader = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($header)); $base64UrlClaims = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($claims)); - + $privateKeyResource = openssl_pkey_get_private(file_get_contents($this->authKey)); - if (!$privateKeyResource) { + if (! $privateKeyResource) { throw new \Exception('Invalid private key'); } - + $signature = ''; $success = openssl_sign("$base64UrlHeader.$base64UrlClaims", $signature, $privateKeyResource, OPENSSL_ALGO_SHA256); - - if (!$success) { + + if (! $success) { throw new \Exception('Failed to sign JWT'); } - + $base64UrlSignature = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($signature)); - + return "$base64UrlHeader.$base64UrlClaims.$base64UrlSignature"; } - } diff --git a/src/Utopia/Messaging/Adapters/Push/FCM.php b/src/Utopia/Messaging/Adapters/Push/FCM.php index 3734b16d..8a4d8ac2 100644 --- a/src/Utopia/Messaging/Adapters/Push/FCM.php +++ b/src/Utopia/Messaging/Adapters/Push/FCM.php @@ -15,11 +15,21 @@ public function __construct( ) { } + /** + * Get adapter name. + * + * @return string + */ public function getName(): string { return 'FCM'; } + /** + * Get max messages per request. + * + * @return int + */ public function getMaxMessagesPerRequest(): int { return 1000; diff --git a/src/Utopia/Messaging/Entities/Entity.php b/src/Utopia/Messaging/Entities/Entity.php deleted file mode 100644 index 126407e2..00000000 --- a/src/Utopia/Messaging/Entities/Entity.php +++ /dev/null @@ -1,7 +0,0 @@ -id = $id; - $this->providerId = $providerId; - $this->providerInternalId = $providerInternalId; - $this->data = $data; - $this->to = $to; - $this->deliveryTime = $deliveryTime; - $this->deliveryError = $deliveryError; - $this->deliveredTo = $deliveredTo; - $this->delivered = $delivered; - $this->search = $search; - } - -} \ No newline at end of file diff --git a/src/Utopia/Messaging/Entities/Provider.php b/src/Utopia/Messaging/Entities/Provider.php deleted file mode 100644 index d85b604e..00000000 --- a/src/Utopia/Messaging/Entities/Provider.php +++ /dev/null @@ -1,35 +0,0 @@ -id = $id; - $this->userId = $userId; - $this->name = $name; - $this->provider = $provider; - $this->type = $type; - $this->credentials = $credentials; - } - -} \ No newline at end of file diff --git a/src/Utopia/Messaging/Entities/Subscriber.php b/src/Utopia/Messaging/Entities/Subscriber.php deleted file mode 100644 index d7594427..00000000 --- a/src/Utopia/Messaging/Entities/Subscriber.php +++ /dev/null @@ -1,31 +0,0 @@ -id = $id; - $this->userId = $userId; - $this->userInternalId = $userInternalId; - $this->targetId = $targetId; - $this->targetInternalId = $targetInternalId; - } - -} \ No newline at end of file diff --git a/src/Utopia/Messaging/Entities/Target.php b/src/Utopia/Messaging/Entities/Target.php deleted file mode 100644 index b0d37a35..00000000 --- a/src/Utopia/Messaging/Entities/Target.php +++ /dev/null @@ -1,34 +0,0 @@ -id = $id; - $this->userId = $userId; - $this->userInternalId = $userInternalId; - $this->providerId = $providerId; - $this->providerInternalId = $providerInternalId; - $this->providerType = $providerType; - $this->identifier = $identifier; - } - -} \ No newline at end of file diff --git a/src/Utopia/Messaging/Entities/Topic.php b/src/Utopia/Messaging/Entities/Topic.php deleted file mode 100644 index b9759b8e..00000000 --- a/src/Utopia/Messaging/Entities/Topic.php +++ /dev/null @@ -1,29 +0,0 @@ -id = $id; - $this->providerId = $providerId; - $this->providerInternalId = $providerInternalId; - $this->name = $name; - $this->description = $description; - } - -} \ No newline at end of file diff --git a/src/Utopia/Messaging/Message.php b/src/Utopia/Messaging/Message.php index 0b70a2ce..5c335d17 100644 --- a/src/Utopia/Messaging/Message.php +++ b/src/Utopia/Messaging/Message.php @@ -7,6 +7,7 @@ */ interface Message { - function getTo():array; - function getFrom():?string; + public function getTo(): array; + + public function getFrom(): ?string; } diff --git a/src/Utopia/Messaging/Messages/Push.php b/src/Utopia/Messaging/Messages/Push.php index 12e1d7c3..93b8f5d7 100644 --- a/src/Utopia/Messaging/Messages/Push.php +++ b/src/Utopia/Messaging/Messages/Push.php @@ -44,7 +44,7 @@ public function getFrom(): ?string { return null; } - + /** * @return string */ diff --git a/tests/e2e/Email/MailgunTest.php b/tests/e2e/Email/MailgunTest.php index 0f55ee15..2426b03f 100644 --- a/tests/e2e/Email/MailgunTest.php +++ b/tests/e2e/Email/MailgunTest.php @@ -12,19 +12,18 @@ class MailgunTest extends Base */ public function testSendEmail() { - $key = getenv('MAILGUN_API_KEY'); $domain = getenv('MAILGUN_DOMAIN'); $sender = new Mailgun( - $key, - $domain + $key, + $domain ); $to = 'wcope@me.com'; $subject = 'Test Subject'; $content = 'Test Content'; - $from = 'sender@' . $domain; + $from = 'sender@'.$domain; $message = new Email( to: [$to], diff --git a/tests/e2e/Push/APNSTest.php b/tests/e2e/Push/APNSTest.php index 39fe3dd6..c008df84 100644 --- a/tests/e2e/Push/APNSTest.php +++ b/tests/e2e/Push/APNSTest.php @@ -9,7 +9,7 @@ class APNSTest extends Base { public function testSend(): void { - $authKey = __DIR__ . '/AuthKey_8KVVCLA3HL.p8'; + $authKey = __DIR__.'/AuthKey_8KVVCLA3HL.p8'; $authKeyId = '8KVVCLA3HL'; $teamId = 'ZZJ8NM59TE'; $bundleId = 'io.wess.appwritetest'; diff --git a/tests/e2e/Push/FCMTest.php b/tests/e2e/Push/FCMTest.php index cd10658c..0b429c6e 100644 --- a/tests/e2e/Push/FCMTest.php +++ b/tests/e2e/Push/FCMTest.php @@ -14,17 +14,17 @@ public function testSend(): void $adapter = new FCMAdapter($serverKey); $message = new Push( - ['eJa9AhokQUudfBPJwRx2OX:APA91bE0KbMkXU7a4eCyq1CyN1nR9TwOD5NQIaHADJBMBV1GjOjTfyPywOXKVeKVvvjz6nvB2jASGtRxGJHsM4Z4osoHnTx5IrnxCNUDEH11wsm4vMBiKW0zbugVis1MdtusTu9admrk'], - 'TestTitle', - 'TestBody', - null, - null, - 'default', - null, - null, - null, - '1' - ); + ['eJa9AhokQUudfBPJwRx2OX:APA91bE0KbMkXU7a4eCyq1CyN1nR9TwOD5NQIaHADJBMBV1GjOjTfyPywOXKVeKVvvjz6nvB2jASGtRxGJHsM4Z4osoHnTx5IrnxCNUDEH11wsm4vMBiKW0zbugVis1MdtusTu9admrk'], + 'TestTitle', + 'TestBody', + null, + null, + 'default', + null, + null, + null, + '1' + ); $response = $adapter->send($message); diff --git a/tests/e2e/SMS/Msg91Test.php b/tests/e2e/SMS/Msg91Test.php index 3183d764..07df9b1c 100644 --- a/tests/e2e/SMS/Msg91Test.php +++ b/tests/e2e/SMS/Msg91Test.php @@ -22,6 +22,6 @@ public function testSendSMS() $result = \json_decode($sender->send($message), true); - $this->assertEquals('success', $result["type"]); + $this->assertEquals('success', $result['type']); } } From 43bed4b545159a3c8081457c9ef83b6eb2980ffd Mon Sep 17 00:00:00 2001 From: wess Date: Fri, 11 Aug 2023 11:14:42 -0400 Subject: [PATCH 10/39] Updates gitignore, it was missing a good bit Removes .env from docker-compose file --- .gitignore | 94 +++++++++++++++++++++++++++++++++++++++++++++- docker-compose.yml | 4 +- 2 files changed, 93 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 03bf4c75..3640a16f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,97 @@ .idea vendor -.phpunit.result.cache Makefile .envrc .env -*.p8 \ No newline at end of file +*.p8 + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### macOS Patch ### +# iCloud generated files +*.icloud + +### PHPUnit ### +# Covers PHPUnit +# Reference: https://phpunit.de/ + +# Generated files +.phpunit.result.cache +.phpunit.cache + +# PHPUnit +/app/phpunit.xml +/phpunit.xml + +# Build data +/build/ + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.toptal.com/developers/gitignore/api/macos,linux,windows,phpunit \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index e0a8130f..623519d5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,9 +8,7 @@ services: - ./src:/usr/local/src/src - ./tests:/usr/local/src/tests - ./phpunit.xml:/usr/local/src/phpunit.xml - env_file: - - .env - + maildev: image: appwrite/mailcatcher:1.0.0 ports: From 7f4448c63786c1f50d33181a5b448d84fd6d061c Mon Sep 17 00:00:00 2001 From: wess Date: Fri, 11 Aug 2023 11:16:01 -0400 Subject: [PATCH 11/39] Moves p8 to use env var --- tests/e2e/Push/APNSTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e/Push/APNSTest.php b/tests/e2e/Push/APNSTest.php index c008df84..558c6c89 100644 --- a/tests/e2e/Push/APNSTest.php +++ b/tests/e2e/Push/APNSTest.php @@ -9,7 +9,7 @@ class APNSTest extends Base { public function testSend(): void { - $authKey = __DIR__.'/AuthKey_8KVVCLA3HL.p8'; + $authKey = getenv('AuthKey_8KVVCLA3HL.p8'); $authKeyId = '8KVVCLA3HL'; $teamId = 'ZZJ8NM59TE'; $bundleId = 'io.wess.appwritetest'; From 5d26fde36b1d66c56dec62e719d030ce30c7f851 Mon Sep 17 00:00:00 2001 From: wess Date: Mon, 14 Aug 2023 10:53:27 -0400 Subject: [PATCH 12/39] Adds proper env vars to docker-compose.yml --- docker-compose.yml | 10 ++++++++++ src/Utopia/Messaging/Adapters/Push/APNS.php | 6 +++--- tests/e2e/Push/APNSTest.php | 2 +- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 623519d5..2f915c34 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -4,6 +4,16 @@ services: tests: build: context: . + environment: + - MAILGUN_API_KEY + - MAILGUN_DOMAIN + - SENDGRID_API_KEY + - FCM_SERVER_KEY + - TWILIO_ACCOUNT_SID + - TWILIO_AUTH_TOKEN + - TELNYX_API_KEY + - TELNYX_PUBLIC_KEY + - AUTHKEY_8KVVCLA3HL volumes: - ./src:/usr/local/src/src - ./tests:/usr/local/src/tests diff --git a/src/Utopia/Messaging/Adapters/Push/APNS.php b/src/Utopia/Messaging/Adapters/Push/APNS.php index 8c5b2597..4ec07464 100644 --- a/src/Utopia/Messaging/Adapters/Push/APNS.php +++ b/src/Utopia/Messaging/Adapters/Push/APNS.php @@ -102,13 +102,13 @@ private function generateJwt(): string $base64UrlHeader = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($header)); $base64UrlClaims = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($claims)); - $privateKeyResource = openssl_pkey_get_private(file_get_contents($this->authKey)); - if (! $privateKeyResource) { + if (! $this->authKey) { + var_dump($this->authKey); throw new \Exception('Invalid private key'); } $signature = ''; - $success = openssl_sign("$base64UrlHeader.$base64UrlClaims", $signature, $privateKeyResource, OPENSSL_ALGO_SHA256); + $success = openssl_sign("$base64UrlHeader.$base64UrlClaims", $signature, $this->authKey, OPENSSL_ALGO_SHA256); if (! $success) { throw new \Exception('Failed to sign JWT'); diff --git a/tests/e2e/Push/APNSTest.php b/tests/e2e/Push/APNSTest.php index 558c6c89..b558a156 100644 --- a/tests/e2e/Push/APNSTest.php +++ b/tests/e2e/Push/APNSTest.php @@ -9,7 +9,7 @@ class APNSTest extends Base { public function testSend(): void { - $authKey = getenv('AuthKey_8KVVCLA3HL.p8'); + $authKey = getenv('AUTHKEY_8KVVCLA3HL'); $authKeyId = '8KVVCLA3HL'; $teamId = 'ZZJ8NM59TE'; $bundleId = 'io.wess.appwritetest'; From 0ea850d9033d267cd219a2142791b2b6ba99a7a6 Mon Sep 17 00:00:00 2001 From: wess Date: Mon, 14 Aug 2023 11:36:33 -0400 Subject: [PATCH 13/39] lint all the things! (^_^)/ --- src/Utopia/Messaging/Adapters/Push/APNS.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Utopia/Messaging/Adapters/Push/APNS.php b/src/Utopia/Messaging/Adapters/Push/APNS.php index 4ec07464..e933d30d 100644 --- a/src/Utopia/Messaging/Adapters/Push/APNS.php +++ b/src/Utopia/Messaging/Adapters/Push/APNS.php @@ -103,7 +103,7 @@ private function generateJwt(): string $base64UrlClaims = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($claims)); if (! $this->authKey) { - var_dump($this->authKey); + var_dump($this->authKey); throw new \Exception('Invalid private key'); } From 6f2defbf50173a358bbea9caff6561b0894a320c Mon Sep 17 00:00:00 2001 From: wess Date: Mon, 14 Aug 2023 16:04:20 -0400 Subject: [PATCH 14/39] Let's add EU --- src/Utopia/Messaging/Adapters/Email/Mailgun.php | 5 ++++- tests/e2e/Email/MailgunTest.php | 4 ++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Utopia/Messaging/Adapters/Email/Mailgun.php b/src/Utopia/Messaging/Adapters/Email/Mailgun.php index 5fbdcb01..29e1abb7 100644 --- a/src/Utopia/Messaging/Adapters/Email/Mailgun.php +++ b/src/Utopia/Messaging/Adapters/Email/Mailgun.php @@ -44,9 +44,12 @@ public function getMaxMessagesPerRequest(): int */ protected function process(Email $message): string { + var_dump($this->domain); + exit; + $response = $this->request( method: 'POST', - url: "https://api.mailgun.net/v3/{$this->domain}/messages", + url: "https://api.eu.mailgun.net/v3/{$this->domain}/messages", headers: [ 'Authorization: Basic '.base64_encode('api:'.$this->apiKey), ], diff --git a/tests/e2e/Email/MailgunTest.php b/tests/e2e/Email/MailgunTest.php index 2426b03f..b4cb27c0 100644 --- a/tests/e2e/Email/MailgunTest.php +++ b/tests/e2e/Email/MailgunTest.php @@ -15,6 +15,10 @@ public function testSendEmail() $key = getenv('MAILGUN_API_KEY'); $domain = getenv('MAILGUN_DOMAIN'); + var_dump($key); + var_dump($domain); + die; + $sender = new Mailgun( $key, $domain From 5f85757316eb842e9169d318e234124ff252c404 Mon Sep 17 00:00:00 2001 From: wess Date: Mon, 14 Aug 2023 16:35:31 -0400 Subject: [PATCH 15/39] Fixes lints and boom passing tests --- tests/e2e/Email/MailgunTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/e2e/Email/MailgunTest.php b/tests/e2e/Email/MailgunTest.php index b4cb27c0..db4b6115 100644 --- a/tests/e2e/Email/MailgunTest.php +++ b/tests/e2e/Email/MailgunTest.php @@ -15,9 +15,9 @@ public function testSendEmail() $key = getenv('MAILGUN_API_KEY'); $domain = getenv('MAILGUN_DOMAIN'); - var_dump($key); - var_dump($domain); - die; + var_dump($key); + var_dump($domain); + exit; $sender = new Mailgun( $key, From b8add141eebc67242658b3bf500ed30d858b6345 Mon Sep 17 00:00:00 2001 From: wess Date: Wed, 30 Aug 2023 08:35:23 -0400 Subject: [PATCH 16/39] PR requested updates APNS rewrite for batch sending using HTTP/2 --- docker-compose.yml | 12 +-- phpunit.xml | 2 +- .../Messaging/Adapters/Email/Mailgun.php | 10 ++- src/Utopia/Messaging/Adapters/Push/APNS.php | 88 ++++++++++++++++--- .../Messaging/Adapters/SMS/Telesign.php | 18 +++- src/Utopia/Messaging/Adapters/SMS/Vonage.php | 5 +- tests/e2e/Email/MailgunTest.php | 12 ++- tests/e2e/Push/APNSTest.php | 28 +++--- tests/e2e/Push/FCMTest.php | 24 ++--- tests/e2e/SMS/TelesignTest.php | 17 +--- tests/e2e/SMS/TwilioTest.php | 11 ++- tests/e2e/SMS/VonageTest.php | 32 +++++++ tests/e2e/SMS/vonage.php | 29 ------ 13 files changed, 173 insertions(+), 115 deletions(-) create mode 100644 tests/e2e/SMS/VonageTest.php delete mode 100644 tests/e2e/SMS/vonage.php diff --git a/docker-compose.yml b/docker-compose.yml index 2f915c34..11ef5e2c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -4,16 +4,8 @@ services: tests: build: context: . - environment: - - MAILGUN_API_KEY - - MAILGUN_DOMAIN - - SENDGRID_API_KEY - - FCM_SERVER_KEY - - TWILIO_ACCOUNT_SID - - TWILIO_AUTH_TOKEN - - TELNYX_API_KEY - - TELNYX_PUBLIC_KEY - - AUTHKEY_8KVVCLA3HL + env_file: + - .env volumes: - ./src:/usr/local/src/src - ./tests:/usr/local/src/tests diff --git a/phpunit.xml b/phpunit.xml index 53cd4abd..8db6586a 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -6,7 +6,7 @@ convertNoticesToExceptions="true" convertWarningsToExceptions="true" processIsolation="false" - stopOnFailure="true" + stopOnFailure="false" > diff --git a/src/Utopia/Messaging/Adapters/Email/Mailgun.php b/src/Utopia/Messaging/Adapters/Email/Mailgun.php index 29e1abb7..a41e4dd9 100644 --- a/src/Utopia/Messaging/Adapters/Email/Mailgun.php +++ b/src/Utopia/Messaging/Adapters/Email/Mailgun.php @@ -42,14 +42,16 @@ public function getMaxMessagesPerRequest(): int * * @throws \Exception */ - protected function process(Email $message): string + protected function process(Email $message, bool $isUS = true): string { - var_dump($this->domain); - exit; + $usDomain = 'api.mailgun.net'; + $euDomain = 'api.eu.mailgun.net'; + $domain = $isUS ? $usDomain : $euDomain; + $response = $this->request( method: 'POST', - url: "https://api.eu.mailgun.net/v3/{$this->domain}/messages", + url: "https://$domain/v3/{$this->domain}/messages", headers: [ 'Authorization: Basic '.base64_encode('api:'.$this->apiKey), ], diff --git a/src/Utopia/Messaging/Adapters/Push/APNS.php b/src/Utopia/Messaging/Adapters/Push/APNS.php index e933d30d..288b329d 100644 --- a/src/Utopia/Messaging/Adapters/Push/APNS.php +++ b/src/Utopia/Messaging/Adapters/Push/APNS.php @@ -55,12 +55,7 @@ public function getMaxMessagesPerRequest(): int */ public function process(Push $message): string { - $headers = [ - 'authorization: bearer '.$this->generateJwt(), - 'apns-topic: '.$this->bundleId, - ]; - - $payload = json_encode([ + $payload = [ 'aps' => [ 'alert' => [ 'title' => $message->getTitle(), @@ -70,18 +65,82 @@ public function process(Push $message): string 'sound' => $message->getSound(), 'data' => $message->getData(), ], - ]); + ]; // Assuming the 'to' array contains device tokens for the push notification recipients. - foreach ($message->getTo() as $to) { - $url = $this->endpoint.'/3/device/'.$to; - $response = $this->request('POST', $url, $headers, $payload); + + // $url = $this->endpoint.'/3/device'; + // $response = $this->request('POST', $url, $headers, \json_encode($payloads)); + // // This example simply returns the last response, adjust as needed + // return $response; + + return $this->notify($message->getTo(), $payload); + } + + private function notify(array $to, array $payload) + { + $headers = [ + 'authorization: bearer '.$this->generateJwt(), + 'apns-topic: '.$this->bundleId, + ]; + + $ch = curl_init(); + + curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0); + + curl_setopt_array($ch, array( + CURLOPT_PORT => 443, + CURLOPT_HTTPHEADER => $headers, + CURLOPT_POST => TRUE, + CURLOPT_POSTFIELDS => \json_encode($payload), + CURLOPT_RETURNTRANSFER => TRUE, + CURLOPT_TIMEOUT => 30, + CURLOPT_HEADER => TRUE + )); + + $response = ''; + + foreach($to as $token) { + curl_setopt($ch, CURLOPT_URL, $this->endpoint.'/3/device/'.$token); - // You might want to handle each response here, for instance, logging failures + $response = curl_exec($ch); + } + + curl_close($ch); + + $response = $this->formatResponse($response); + + var_dump($response); + die; + return $response; + } + + private function formatResponse(string $response):array + { + $filtered = array_filter( + explode("\r\n", $response), + function($value) { + return !empty($value); } + ); + + $result = []; + + foreach($filtered as $value) { + if(str_contains($value, 'HTTP')) { + $result['status'] = trim(str_replace('HTTP/2 ', '', $value)); + continue; + } + + $parts = explode(':', trim($value)); + + $result[$parts[0]] = $parts[1]; + } + + var_dump($result); + die; - // This example simply returns the last response, adjust as needed - return $response; + return $result; } /** @@ -99,11 +158,12 @@ private function generateJwt(): string 'iat' => time(), ]); + // Replaces URL sensitive characters that could be the result of base64 encoding. + // Replace to _ to avoid any special handling. $base64UrlHeader = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($header)); $base64UrlClaims = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($claims)); if (! $this->authKey) { - var_dump($this->authKey); throw new \Exception('Invalid private key'); } diff --git a/src/Utopia/Messaging/Adapters/SMS/Telesign.php b/src/Utopia/Messaging/Adapters/SMS/Telesign.php index d19d5876..6cd83ac3 100644 --- a/src/Utopia/Messaging/Adapters/SMS/Telesign.php +++ b/src/Utopia/Messaging/Adapters/SMS/Telesign.php @@ -37,10 +37,10 @@ public function getMaxMessagesPerRequest(): int */ protected function process(SMS $message): string { - $to = \array_map( - fn ($to) => \ltrim($to, '+'), + $to = $this->formatNumbers(\array_map( + fn ($to) => $to, $message->getTo() - ); + )); return $this->request( method: 'POST', @@ -50,8 +50,18 @@ protected function process(SMS $message): string ], body: \http_build_query([ 'template' => $message->getContent(), - 'recipients' => \implode(',', $to), + 'recipients' => $to, ]), ); } + + private function formatNumbers(array $numbers): string + { + $formatted = \array_map( + fn ($number) => $number . ':' . \uniqid(), + $numbers + ); + + return implode(',', $formatted); + } } diff --git a/src/Utopia/Messaging/Adapters/SMS/Vonage.php b/src/Utopia/Messaging/Adapters/SMS/Vonage.php index 2643a6b0..b724aae7 100644 --- a/src/Utopia/Messaging/Adapters/SMS/Vonage.php +++ b/src/Utopia/Messaging/Adapters/SMS/Vonage.php @@ -37,18 +37,19 @@ public function getMaxMessagesPerRequest(): int */ protected function process(SMS $message): string { + $to = \array_map( fn ($to) => \ltrim($to, '+'), $message->getTo() ); - + return $this->request( method: 'POST', url: 'https://rest.nexmo.com/sms/json', body: \http_build_query([ 'text' => $message->getContent(), 'from' => $message->getFrom(), - 'to' => \implode(',', $to), + 'to' => $to[0], //\implode(',', $to), 'api_key' => $this->apiKey, 'api_secret' => $this->apiSecret, ]), diff --git a/tests/e2e/Email/MailgunTest.php b/tests/e2e/Email/MailgunTest.php index db4b6115..779e1bd8 100644 --- a/tests/e2e/Email/MailgunTest.php +++ b/tests/e2e/Email/MailgunTest.php @@ -15,10 +15,6 @@ public function testSendEmail() $key = getenv('MAILGUN_API_KEY'); $domain = getenv('MAILGUN_DOMAIN'); - var_dump($key); - var_dump($domain); - exit; - $sender = new Mailgun( $key, $domain @@ -36,8 +32,10 @@ public function testSendEmail() content: $content, ); - $sender->send($message); - - $this->assertEquals(true, true); + $result = (array)\json_decode($sender->send($message)); + + $this->assertArrayHasKey('id', $result); + $this->assertArrayHasKey('message', $result); + $this->assertTrue(str_contains(strtolower($result['message']), 'queued')); } } diff --git a/tests/e2e/Push/APNSTest.php b/tests/e2e/Push/APNSTest.php index b558a156..4212a3a5 100644 --- a/tests/e2e/Push/APNSTest.php +++ b/tests/e2e/Push/APNSTest.php @@ -9,25 +9,25 @@ class APNSTest extends Base { public function testSend(): void { - $authKey = getenv('AUTHKEY_8KVVCLA3HL'); - $authKeyId = '8KVVCLA3HL'; - $teamId = 'ZZJ8NM59TE'; - $bundleId = 'io.wess.appwritetest'; + $authKey = getenv('APNS_AUTHKEY_8KVVCLA3HL'); + $authKeyId = getenv('APNS_AUTH_ID'); + $teamId = getenv('APNS_TEAM_ID'); + $bundleId = getenv('APNS_BUNDLE_ID'); $endpoint = 'https://api.sandbox.push.apple.com:443'; $adapter = new APNSAdapter($authKey, $authKeyId, $teamId, $bundleId, $endpoint); $message = new Push( - ['80858be082476f1067ce737b69240bbea9b58676d0eef64960f3aa75b6cb7ca7656822f02a56960ff805b393ab7c82484f56229b69731f939ae8c3aa27399c29fe8efa26d272b5a1817813f023dee9fd'], - 'TestTitle', - 'TestBody', - null, - null, - 'default', - null, - null, - null, - '1' + to: ['80858be082476f1067ce737b69240bbea9b58676d0eef64960f3aa75b6cb7ca7656822f02a56960ff805b393ab7c82484f56229b69731f939ae8c3aa27399c29fe8efa26d272b5a1817813f023dee9fd'], + title: 'TestTitle', + body: 'TestBody', + data: null, + action: null, + sound: 'default', + icon: null, + color: null, + tag: null, + badge: '1' ); $response = $adapter->send($message); diff --git a/tests/e2e/Push/FCMTest.php b/tests/e2e/Push/FCMTest.php index 0b429c6e..71128e45 100644 --- a/tests/e2e/Push/FCMTest.php +++ b/tests/e2e/Push/FCMTest.php @@ -14,20 +14,22 @@ public function testSend(): void $adapter = new FCMAdapter($serverKey); $message = new Push( - ['eJa9AhokQUudfBPJwRx2OX:APA91bE0KbMkXU7a4eCyq1CyN1nR9TwOD5NQIaHADJBMBV1GjOjTfyPywOXKVeKVvvjz6nvB2jASGtRxGJHsM4Z4osoHnTx5IrnxCNUDEH11wsm4vMBiKW0zbugVis1MdtusTu9admrk'], - 'TestTitle', - 'TestBody', - null, - null, - 'default', - null, - null, - null, - '1' + to: ['eJa9AhokQUudfBPJwRx2OX:APA91bE0KbMkXU7a4eCyq1CyN1nR9TwOD5NQIaHADJBMBV1GjOjTfyPywOXKVeKVvvjz6nvB2jASGtRxGJHsM4Z4osoHnTx5IrnxCNUDEH11wsm4vMBiKW0zbugVis1MdtusTu9admrk'], + title: 'TestTitle', + body: 'TestBody', + data: null, + action: null, + sound: 'default', + icon: null, + color: null, + tag: null, + badge: '1' ); - $response = $adapter->send($message); + $response = \json_decode($adapter->send($message)); $this->assertNotEmpty($response); + $this->assertEquals(1, $response->success); + $this->assertEquals(0, $response->failure); } } diff --git a/tests/e2e/SMS/TelesignTest.php b/tests/e2e/SMS/TelesignTest.php index 7cf276f9..f78653e0 100644 --- a/tests/e2e/SMS/TelesignTest.php +++ b/tests/e2e/SMS/TelesignTest.php @@ -12,21 +12,6 @@ class TelesignTest extends Base */ public function testSendSMS() { - // $username = ''; - // $password = ''; - - // $sender = new Telesign($username, $password); - - // $message = new SMS( - // to: ['+18034041123'], - // content: 'Test Content', - // from: '+15005550006' - // ); - - // $result = \json_decode($sender->send($message), true); - - // $this->assertEquals('success', $result["type"]); - - $this->markTestSkipped('Telesign requires sales calls and such to setup an account'); + $this->markTestSkipped('Telesign requires support/sales call in order to enable bulk SMS'); } } diff --git a/tests/e2e/SMS/TwilioTest.php b/tests/e2e/SMS/TwilioTest.php index 3c50ae4e..c3b86760 100644 --- a/tests/e2e/SMS/TwilioTest.php +++ b/tests/e2e/SMS/TwilioTest.php @@ -13,15 +13,20 @@ class TwilioTest extends Base public function testSendSMS() { $sender = new Twilio(getenv('TWILIO_ACCOUNT_SID'), getenv('TWILIO_AUTH_TOKEN')); + $to = ['+18034041123']; + $from = '+15005550006'; $message = new SMS( - to: ['+18034041123'], + to: $to, content: 'Test Content', - from: '+15005550006' + from: $from ); - $result = $sender->send($message); + $result = \json_decode($sender->send($message)); $this->assertNotEmpty($result); + $this->assertEquals($to[0], $result->to); + $this->assertEquals($from, $result->from); + $this->assertNull($result->error_message); } } diff --git a/tests/e2e/SMS/VonageTest.php b/tests/e2e/SMS/VonageTest.php new file mode 100644 index 00000000..9fc71034 --- /dev/null +++ b/tests/e2e/SMS/VonageTest.php @@ -0,0 +1,32 @@ +send($message), true); + + $this->assertArrayHasKey("messages", $result); + $this->assertEquals(1, count($result['messages'])); + $this->assertEquals("1", $result['message-count']); + } +} diff --git a/tests/e2e/SMS/vonage.php b/tests/e2e/SMS/vonage.php deleted file mode 100644 index dc175df9..00000000 --- a/tests/e2e/SMS/vonage.php +++ /dev/null @@ -1,29 +0,0 @@ -send($message), true); - - // $this->assertEquals('success', $result["type"]); - - $this->markTestSkipped('Requires a business account, and to contact them first before getting access.'); - } -} From da94fa79a62d20c500a3c45cbddd66b7e48c0749 Mon Sep 17 00:00:00 2001 From: wess Date: Wed, 30 Aug 2023 08:51:53 -0400 Subject: [PATCH 17/39] Correction and clean up for APNS.php Updated tests to look for keys and status --- src/Utopia/Messaging/Adapters/Push/APNS.php | 13 +++---------- tests/e2e/Push/APNSTest.php | 6 ++++-- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/src/Utopia/Messaging/Adapters/Push/APNS.php b/src/Utopia/Messaging/Adapters/Push/APNS.php index 288b329d..fc24c585 100644 --- a/src/Utopia/Messaging/Adapters/Push/APNS.php +++ b/src/Utopia/Messaging/Adapters/Push/APNS.php @@ -74,10 +74,10 @@ public function process(Push $message): string // // This example simply returns the last response, adjust as needed // return $response; - return $this->notify($message->getTo(), $payload); + return \json_encode($this->notify($message->getTo(), $payload)); } - private function notify(array $to, array $payload) + private function notify(array $to, array $payload):array { $headers = [ 'authorization: bearer '.$this->generateJwt(), @@ -108,11 +108,7 @@ private function notify(array $to, array $payload) curl_close($ch); - $response = $this->formatResponse($response); - - var_dump($response); - die; - return $response; + return $this->formatResponse($response); } private function formatResponse(string $response):array @@ -137,9 +133,6 @@ function($value) { $result[$parts[0]] = $parts[1]; } - var_dump($result); - die; - return $result; } diff --git a/tests/e2e/Push/APNSTest.php b/tests/e2e/Push/APNSTest.php index 4212a3a5..b666e658 100644 --- a/tests/e2e/Push/APNSTest.php +++ b/tests/e2e/Push/APNSTest.php @@ -30,8 +30,10 @@ public function testSend(): void badge: '1' ); - $response = $adapter->send($message); + $response = (array)\json_decode($adapter->send($message)); - $this->assertEquals('', $response); + $this->assertEquals("200", $response['status']); + $this->assertArrayHasKey('apns-id', $response); + $this->assertArrayHasKey('apns-unique-id', $response); } } From d43325af2669c8c3904d682e7116a309c0d46799 Mon Sep 17 00:00:00 2001 From: wess Date: Wed, 30 Aug 2023 10:53:34 -0400 Subject: [PATCH 18/39] Removes personal email and moves test emails into .env --- tests/e2e/Email/MailgunTest.php | 2 +- tests/e2e/Email/SendgridTest.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/e2e/Email/MailgunTest.php b/tests/e2e/Email/MailgunTest.php index 779e1bd8..2f880992 100644 --- a/tests/e2e/Email/MailgunTest.php +++ b/tests/e2e/Email/MailgunTest.php @@ -20,7 +20,7 @@ public function testSendEmail() $domain ); - $to = 'wcope@me.com'; + $to = getenv('TEST_EMAIL'); $subject = 'Test Subject'; $content = 'Test Content'; $from = 'sender@'.$domain; diff --git a/tests/e2e/Email/SendgridTest.php b/tests/e2e/Email/SendgridTest.php index 0786a1eb..05bcc3ab 100644 --- a/tests/e2e/Email/SendgridTest.php +++ b/tests/e2e/Email/SendgridTest.php @@ -15,10 +15,10 @@ public function testSendEmail() $key = getenv('SENDGRID_API_KEY'); $sender = new Sendgrid($key); - $to = 'wcope@me.com'; + $to = getenv('TEST_EMAIL'); $subject = 'Test Subject'; $content = 'Test Content'; - $from = 'devpipe@wess.io'; + $from = getenv('TEST_FROM_EMAIL'); $message = new Email( to: [$to], From 36aee6ae3c601b796364e35be083b909e02bef66 Mon Sep 17 00:00:00 2001 From: wess Date: Wed, 30 Aug 2023 14:32:54 -0400 Subject: [PATCH 19/39] Lint fixes --- .../Messaging/Adapters/Email/Mailgun.php | 2 +- src/Utopia/Messaging/Adapters/Push/APNS.php | 83 ++++++++++--------- .../Messaging/Adapters/SMS/Telesign.php | 4 +- src/Utopia/Messaging/Adapters/SMS/Vonage.php | 3 +- tests/e2e/Email/MailgunTest.php | 4 +- tests/e2e/Push/APNSTest.php | 4 +- tests/e2e/SMS/TelesignTest.php | 3 - tests/e2e/SMS/VonageTest.php | 8 +- 8 files changed, 54 insertions(+), 57 deletions(-) diff --git a/src/Utopia/Messaging/Adapters/Email/Mailgun.php b/src/Utopia/Messaging/Adapters/Email/Mailgun.php index a41e4dd9..112d471f 100644 --- a/src/Utopia/Messaging/Adapters/Email/Mailgun.php +++ b/src/Utopia/Messaging/Adapters/Email/Mailgun.php @@ -48,7 +48,7 @@ protected function process(Email $message, bool $isUS = true): string $euDomain = 'api.eu.mailgun.net'; $domain = $isUS ? $usDomain : $euDomain; - + $response = $this->request( method: 'POST', url: "https://$domain/v3/{$this->domain}/messages", diff --git a/src/Utopia/Messaging/Adapters/Push/APNS.php b/src/Utopia/Messaging/Adapters/Push/APNS.php index fc24c585..4f8dda83 100644 --- a/src/Utopia/Messaging/Adapters/Push/APNS.php +++ b/src/Utopia/Messaging/Adapters/Push/APNS.php @@ -68,7 +68,7 @@ public function process(Push $message): string ]; // Assuming the 'to' array contains device tokens for the push notification recipients. - + // $url = $this->endpoint.'/3/device'; // $response = $this->request('POST', $url, $headers, \json_encode($payloads)); // // This example simply returns the last response, adjust as needed @@ -77,63 +77,64 @@ public function process(Push $message): string return \json_encode($this->notify($message->getTo(), $payload)); } - private function notify(array $to, array $payload):array + private function notify(array $to, array $payload): array { - $headers = [ - 'authorization: bearer '.$this->generateJwt(), - 'apns-topic: '.$this->bundleId, - ]; + $headers = [ + 'authorization: bearer '.$this->generateJwt(), + 'apns-topic: '.$this->bundleId, + ]; - $ch = curl_init(); + $ch = curl_init(); - curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0); + curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0); - curl_setopt_array($ch, array( - CURLOPT_PORT => 443, - CURLOPT_HTTPHEADER => $headers, - CURLOPT_POST => TRUE, - CURLOPT_POSTFIELDS => \json_encode($payload), - CURLOPT_RETURNTRANSFER => TRUE, - CURLOPT_TIMEOUT => 30, - CURLOPT_HEADER => TRUE - )); + curl_setopt_array($ch, [ + CURLOPT_PORT => 443, + CURLOPT_HTTPHEADER => $headers, + CURLOPT_POST => true, + CURLOPT_POSTFIELDS => \json_encode($payload), + CURLOPT_RETURNTRANSFER => true, + CURLOPT_TIMEOUT => 30, + CURLOPT_HEADER => true, + ]); - $response = ''; + $response = ''; - foreach($to as $token) { - curl_setopt($ch, CURLOPT_URL, $this->endpoint.'/3/device/'.$token); + foreach ($to as $token) { + curl_setopt($ch, CURLOPT_URL, $this->endpoint.'/3/device/'.$token); - $response = curl_exec($ch); - } + $response = curl_exec($ch); + } - curl_close($ch); + curl_close($ch); - return $this->formatResponse($response); + return $this->formatResponse($response); } - private function formatResponse(string $response):array + private function formatResponse(string $response): array { - $filtered = array_filter( - explode("\r\n", $response), - function($value) { - return !empty($value); - } - ); + $filtered = array_filter( + explode("\r\n", $response), + function ($value) { + return ! empty($value); + } + ); - $result = []; + $result = []; - foreach($filtered as $value) { - if(str_contains($value, 'HTTP')) { - $result['status'] = trim(str_replace('HTTP/2 ', '', $value)); - continue; - } + foreach ($filtered as $value) { + if (str_contains($value, 'HTTP')) { + $result['status'] = trim(str_replace('HTTP/2 ', '', $value)); - $parts = explode(':', trim($value)); + continue; + } - $result[$parts[0]] = $parts[1]; - } + $parts = explode(':', trim($value)); + + $result[$parts[0]] = $parts[1]; + } - return $result; + return $result; } /** diff --git a/src/Utopia/Messaging/Adapters/SMS/Telesign.php b/src/Utopia/Messaging/Adapters/SMS/Telesign.php index 6cd83ac3..6e77903e 100644 --- a/src/Utopia/Messaging/Adapters/SMS/Telesign.php +++ b/src/Utopia/Messaging/Adapters/SMS/Telesign.php @@ -58,10 +58,10 @@ protected function process(SMS $message): string private function formatNumbers(array $numbers): string { $formatted = \array_map( - fn ($number) => $number . ':' . \uniqid(), + fn ($number) => $number.':'.\uniqid(), $numbers ); return implode(',', $formatted); - } + } } diff --git a/src/Utopia/Messaging/Adapters/SMS/Vonage.php b/src/Utopia/Messaging/Adapters/SMS/Vonage.php index b724aae7..7ed16714 100644 --- a/src/Utopia/Messaging/Adapters/SMS/Vonage.php +++ b/src/Utopia/Messaging/Adapters/SMS/Vonage.php @@ -37,12 +37,11 @@ public function getMaxMessagesPerRequest(): int */ protected function process(SMS $message): string { - $to = \array_map( fn ($to) => \ltrim($to, '+'), $message->getTo() ); - + return $this->request( method: 'POST', url: 'https://rest.nexmo.com/sms/json', diff --git a/tests/e2e/Email/MailgunTest.php b/tests/e2e/Email/MailgunTest.php index 2f880992..250d98d7 100644 --- a/tests/e2e/Email/MailgunTest.php +++ b/tests/e2e/Email/MailgunTest.php @@ -32,8 +32,8 @@ public function testSendEmail() content: $content, ); - $result = (array)\json_decode($sender->send($message)); - + $result = (array) \json_decode($sender->send($message)); + $this->assertArrayHasKey('id', $result); $this->assertArrayHasKey('message', $result); $this->assertTrue(str_contains(strtolower($result['message']), 'queued')); diff --git a/tests/e2e/Push/APNSTest.php b/tests/e2e/Push/APNSTest.php index b666e658..1f7b860f 100644 --- a/tests/e2e/Push/APNSTest.php +++ b/tests/e2e/Push/APNSTest.php @@ -30,9 +30,9 @@ public function testSend(): void badge: '1' ); - $response = (array)\json_decode($adapter->send($message)); + $response = (array) \json_decode($adapter->send($message)); - $this->assertEquals("200", $response['status']); + $this->assertEquals('200', $response['status']); $this->assertArrayHasKey('apns-id', $response); $this->assertArrayHasKey('apns-unique-id', $response); } diff --git a/tests/e2e/SMS/TelesignTest.php b/tests/e2e/SMS/TelesignTest.php index f78653e0..30d2054a 100644 --- a/tests/e2e/SMS/TelesignTest.php +++ b/tests/e2e/SMS/TelesignTest.php @@ -2,9 +2,6 @@ namespace Tests\E2E; -use Utopia\Messaging\Adapters\SMS\Telesign; -use Utopia\Messaging\Messages\SMS; - class TelesignTest extends Base { /** diff --git a/tests/e2e/SMS/VonageTest.php b/tests/e2e/SMS/VonageTest.php index 9fc71034..7f812759 100644 --- a/tests/e2e/SMS/VonageTest.php +++ b/tests/e2e/SMS/VonageTest.php @@ -12,8 +12,8 @@ class VonageTest extends Base */ public function testSendSMS() { - $apiKey = "9bae1fbc"; - $apiSecret = "thVmE4JpL4sPu4f6"; + $apiKey = '9bae1fbc'; + $apiSecret = 'thVmE4JpL4sPu4f6'; $sender = new Vonage($apiKey, $apiSecret); @@ -25,8 +25,8 @@ public function testSendSMS() $result = \json_decode($sender->send($message), true); - $this->assertArrayHasKey("messages", $result); + $this->assertArrayHasKey('messages', $result); $this->assertEquals(1, count($result['messages'])); - $this->assertEquals("1", $result['message-count']); + $this->assertEquals('1', $result['message-count']); } } From ab0a41cf13b173b269bcc1f6264de3a18f1dd765 Mon Sep 17 00:00:00 2001 From: wess Date: Thu, 31 Aug 2023 11:31:47 -0400 Subject: [PATCH 20/39] Removes .env from docker compose --- docker-compose.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 11ef5e2c..623519d5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -4,8 +4,6 @@ services: tests: build: context: . - env_file: - - .env volumes: - ./src:/usr/local/src/src - ./tests:/usr/local/src/tests From 4ebebe97d80bb1de10d362c2464ba28717d333ac Mon Sep 17 00:00:00 2001 From: wess Date: Fri, 1 Sep 2023 10:13:03 -0400 Subject: [PATCH 21/39] Adds US flag to mailgun for region handling --- src/Utopia/Messaging/Adapters/Email/Mailgun.php | 5 +++-- tests/e2e/Email/MailgunTest.php | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Utopia/Messaging/Adapters/Email/Mailgun.php b/src/Utopia/Messaging/Adapters/Email/Mailgun.php index 112d471f..21c70118 100644 --- a/src/Utopia/Messaging/Adapters/Email/Mailgun.php +++ b/src/Utopia/Messaging/Adapters/Email/Mailgun.php @@ -14,6 +14,7 @@ class Mailgun extends EmailAdapter public function __construct( private string $apiKey, private string $domain, + private bool $isUS = true ) { } @@ -42,12 +43,12 @@ public function getMaxMessagesPerRequest(): int * * @throws \Exception */ - protected function process(Email $message, bool $isUS = true): string + protected function process(Email $message): string { $usDomain = 'api.mailgun.net'; $euDomain = 'api.eu.mailgun.net'; - $domain = $isUS ? $usDomain : $euDomain; + $domain = $this->isUS ? $usDomain : $euDomain; $response = $this->request( method: 'POST', diff --git a/tests/e2e/Email/MailgunTest.php b/tests/e2e/Email/MailgunTest.php index 250d98d7..81ae6497 100644 --- a/tests/e2e/Email/MailgunTest.php +++ b/tests/e2e/Email/MailgunTest.php @@ -16,8 +16,9 @@ public function testSendEmail() $domain = getenv('MAILGUN_DOMAIN'); $sender = new Mailgun( - $key, - $domain + apiKey: $key, + domain: $domain, + isUS: false ); $to = getenv('TEST_EMAIL'); From a1a1fb30a0b5e3888251e225d96b177f96a752b0 Mon Sep 17 00:00:00 2001 From: wess Date: Thu, 7 Sep 2023 09:07:17 -0400 Subject: [PATCH 22/39] Forwards env files in docker-composer.yml --- docker-compose.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/docker-compose.yml b/docker-compose.yml index 623519d5..238cd4c1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,6 +2,23 @@ version: '3.9' services: tests: + environment: + - MAILGUN_API_KEY + - MAILGUN_DOMAIN + - SENDGRID_API_KEY + - FCM_SERVER_KEY + - TWILIO_ACCOUNT_SID + - TWILIO_AUTH_TOKEN + - TELNYX_API_KEY + - TELNYX_PUBLIC_KEY + - APNS_AUTHKEY_8KVVCLA3HL + - APNS_AUTH_ID + - APNS_TEAM_ID + - APNS_BUNDLE_ID + - MSG_91_SENDER_ID + - MSG_91_AUTH_KEY + - TEST_EMAIL + - TEST_FROM_EMAIL build: context: . volumes: From 1c5a451cb7c369ca15d610aeb823891642f81d14 Mon Sep 17 00:00:00 2001 From: wess Date: Thu, 7 Sep 2023 09:34:25 -0400 Subject: [PATCH 23/39] Updates workflow for runtime env vars --- .github/workflows/tests.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 901e25bf..4e0100fa 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -13,6 +13,23 @@ jobs: fetch-depth: 2 - run: git checkout HEAD^2 - name: Run Tests + env: + MAILGUN_API_KEY: ${{ secrets.MAILGUN_API_KEY }} + MAILGUN_DOMAIN: ${{ secrets.MAILGUN_DOMAIN }} + SENDGRID_API_KEY: ${{ secrets.SENDGRID_API_KEY }} + FCM_SERVER_KEY: ${{ secrets.FCM_SERVER_KEY }} + TWILIO_ACCOUNT_SID: ${{ secrets.TWILIO_ACCOUNT_SID }} + TWILIO_AUTH_TOKEN: ${{ secrets.TWILIO_AUTH_TOKEN }} + TELNYX_API_KEY: ${{ secrets.TELNYX_API_KEY }} + TELNYX_PUBLIC_KEY: ${{ secrets.TELNYX_PUBLIC_KEY }} + APNS_AUTHKEY_8KVVCLA3HL: ${{ secrets.APNS_AUTHKEY_8KVVCLA3HL }} + APNS_AUTH_ID: ${{ secrets.APNS_AUTH_ID }} + APNS_TEAM_ID: ${{ secrets.APNS_TEAM_ID }} + APNS_BUNDLE_ID: ${{ secrets.APN_BUNDLE_ID }} + MSG_91_SENDER_ID: ${{ secrets.MSG_91_SENDER_ID }} + MSG_91_AUTH_KEY: ${{ secrets.MSG_91_AUTH_KEY }} + TEST_EMAIL: ${{ secrets.TEST_EMAIL }} + TEST_FROM_EMAIL: ${{ secrets.TEST_FROM_EMAIL }} run: | docker compose up -d --build sleep 5 From 8f0e7608039d8ae8d25dc04c47b8d775c50aab70 Mon Sep 17 00:00:00 2001 From: wess Date: Thu, 7 Sep 2023 09:37:43 -0400 Subject: [PATCH 24/39] isUS, for mailgun, is true --- tests/e2e/Email/MailgunTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e/Email/MailgunTest.php b/tests/e2e/Email/MailgunTest.php index 81ae6497..0a6f018f 100644 --- a/tests/e2e/Email/MailgunTest.php +++ b/tests/e2e/Email/MailgunTest.php @@ -18,7 +18,7 @@ public function testSendEmail() $sender = new Mailgun( apiKey: $key, domain: $domain, - isUS: false + isUS: true ); $to = getenv('TEST_EMAIL'); From b301211e3d34a527dabe490385462c8892ebf094 Mon Sep 17 00:00:00 2001 From: wess Date: Thu, 7 Sep 2023 09:41:40 -0400 Subject: [PATCH 25/39] Type-o in workflow --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 4e0100fa..40e61b52 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -25,7 +25,7 @@ jobs: APNS_AUTHKEY_8KVVCLA3HL: ${{ secrets.APNS_AUTHKEY_8KVVCLA3HL }} APNS_AUTH_ID: ${{ secrets.APNS_AUTH_ID }} APNS_TEAM_ID: ${{ secrets.APNS_TEAM_ID }} - APNS_BUNDLE_ID: ${{ secrets.APN_BUNDLE_ID }} + APNS_BUNDLE_ID: ${{ secrets.APNS_BUNDLE_ID }} MSG_91_SENDER_ID: ${{ secrets.MSG_91_SENDER_ID }} MSG_91_AUTH_KEY: ${{ secrets.MSG_91_AUTH_KEY }} TEST_EMAIL: ${{ secrets.TEST_EMAIL }} From 5e5e06381a4b02601fca9285bb3afc564551c830 Mon Sep 17 00:00:00 2001 From: wess Date: Fri, 8 Sep 2023 18:47:57 -0400 Subject: [PATCH 26/39] Removes dead commented code --- src/Utopia/Messaging/Adapters/Push/APNS.php | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/Utopia/Messaging/Adapters/Push/APNS.php b/src/Utopia/Messaging/Adapters/Push/APNS.php index 4f8dda83..ed89c4da 100644 --- a/src/Utopia/Messaging/Adapters/Push/APNS.php +++ b/src/Utopia/Messaging/Adapters/Push/APNS.php @@ -67,13 +67,6 @@ public function process(Push $message): string ], ]; - // Assuming the 'to' array contains device tokens for the push notification recipients. - - // $url = $this->endpoint.'/3/device'; - // $response = $this->request('POST', $url, $headers, \json_encode($payloads)); - // // This example simply returns the last response, adjust as needed - // return $response; - return \json_encode($this->notify($message->getTo(), $payload)); } From 3da89f1ef37df4bcfa9816bd26fa88af21591f1e Mon Sep 17 00:00:00 2001 From: wess Date: Fri, 8 Sep 2023 19:13:02 -0400 Subject: [PATCH 27/39] Updates requested for PR --- .github/workflows/tests.yml | 9 +++++++++ composer.json | 4 ++-- tests/e2e/Push/APNSTest.php | 2 +- tests/e2e/Push/FCMTest.php | 2 +- tests/e2e/SMS/Msg91Test.php | 6 +++--- tests/e2e/SMS/TwilioTest.php | 4 ++-- tests/e2e/SMS/VonageTest.php | 8 ++++---- 7 files changed, 22 insertions(+), 13 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 40e61b52..86d42d4c 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -20,16 +20,25 @@ jobs: FCM_SERVER_KEY: ${{ secrets.FCM_SERVER_KEY }} TWILIO_ACCOUNT_SID: ${{ secrets.TWILIO_ACCOUNT_SID }} TWILIO_AUTH_TOKEN: ${{ secrets.TWILIO_AUTH_TOKEN }} + TWILIO_TO: ${{ secrets.TWILIO_TO }} + TWILIO_FROM: ${{ secrets.TWILIO_FROM }} TELNYX_API_KEY: ${{ secrets.TELNYX_API_KEY }} TELNYX_PUBLIC_KEY: ${{ secrets.TELNYX_PUBLIC_KEY }} APNS_AUTHKEY_8KVVCLA3HL: ${{ secrets.APNS_AUTHKEY_8KVVCLA3HL }} APNS_AUTH_ID: ${{ secrets.APNS_AUTH_ID }} APNS_TEAM_ID: ${{ secrets.APNS_TEAM_ID }} + APNS_TO: ${{ secrets.APNS_TO }} APNS_BUNDLE_ID: ${{ secrets.APNS_BUNDLE_ID }} MSG_91_SENDER_ID: ${{ secrets.MSG_91_SENDER_ID }} MSG_91_AUTH_KEY: ${{ secrets.MSG_91_AUTH_KEY }} + MSG_91_TO: ${{ secrets.MSG_91_TO}} + MSG_91_FROM: ${{ secrets.MSG_91_FROM }} TEST_EMAIL: ${{ secrets.TEST_EMAIL }} TEST_FROM_EMAIL: ${{ secrets.TEST_FROM_EMAIL }} + VONAGE_API_KEY: ${{ secrets.VONAGE_API_KEY }} + VONAGE_API_SECRET: ${{ secrets.VONAGE_API_SECRET }} + VONAGE_TO: ${{ secrets.VONAGE_TO }} + VONAGE_FROM: ${{ secrets.VONAGE_FROM }} run: | docker compose up -d --build sleep 5 diff --git a/composer.json b/composer.json index 7dfe178e..bac108a5 100644 --- a/composer.json +++ b/composer.json @@ -26,8 +26,8 @@ "ext-curl": "*" }, "require-dev": { - "phpunit/phpunit": "^9.6", - "phpmailer/phpmailer": "^6.8", + "phpunit/phpunit": "9.6.*", + "phpmailer/phpmailer": "6.8.*", "laravel/pint": "^1.2" }, "config": { diff --git a/tests/e2e/Push/APNSTest.php b/tests/e2e/Push/APNSTest.php index 1f7b860f..0be2613c 100644 --- a/tests/e2e/Push/APNSTest.php +++ b/tests/e2e/Push/APNSTest.php @@ -18,7 +18,7 @@ public function testSend(): void $adapter = new APNSAdapter($authKey, $authKeyId, $teamId, $bundleId, $endpoint); $message = new Push( - to: ['80858be082476f1067ce737b69240bbea9b58676d0eef64960f3aa75b6cb7ca7656822f02a56960ff805b393ab7c82484f56229b69731f939ae8c3aa27399c29fe8efa26d272b5a1817813f023dee9fd'], + to: [getenv("APNS_TO")], title: 'TestTitle', body: 'TestBody', data: null, diff --git a/tests/e2e/Push/FCMTest.php b/tests/e2e/Push/FCMTest.php index 71128e45..f3e86668 100644 --- a/tests/e2e/Push/FCMTest.php +++ b/tests/e2e/Push/FCMTest.php @@ -14,7 +14,7 @@ public function testSend(): void $adapter = new FCMAdapter($serverKey); $message = new Push( - to: ['eJa9AhokQUudfBPJwRx2OX:APA91bE0KbMkXU7a4eCyq1CyN1nR9TwOD5NQIaHADJBMBV1GjOjTfyPywOXKVeKVvvjz6nvB2jASGtRxGJHsM4Z4osoHnTx5IrnxCNUDEH11wsm4vMBiKW0zbugVis1MdtusTu9admrk'], + to: [getenv("FCM_SERVER_TO")], title: 'TestTitle', body: 'TestBody', data: null, diff --git a/tests/e2e/SMS/Msg91Test.php b/tests/e2e/SMS/Msg91Test.php index 07df9b1c..582bdf34 100644 --- a/tests/e2e/SMS/Msg91Test.php +++ b/tests/e2e/SMS/Msg91Test.php @@ -12,12 +12,12 @@ class Msg91Test extends Base */ public function testSendSMS() { - $sender = new Msg91('12345', '402985Ajm8DXo3EG4964cd3c10P1'); + $sender = new Msg91(getenv("MSG_91_SENDER_ID"), getenv("MSG_91_AUTH_KEY")); $message = new SMS( - to: ['+18034041123'], + to: [getenv("MSG_91_TO")], content: 'Test Content', - from: '+15005550006' + from: getenv("MSG_91_FROM") ); $result = \json_decode($sender->send($message), true); diff --git a/tests/e2e/SMS/TwilioTest.php b/tests/e2e/SMS/TwilioTest.php index c3b86760..90fad2df 100644 --- a/tests/e2e/SMS/TwilioTest.php +++ b/tests/e2e/SMS/TwilioTest.php @@ -13,8 +13,8 @@ class TwilioTest extends Base public function testSendSMS() { $sender = new Twilio(getenv('TWILIO_ACCOUNT_SID'), getenv('TWILIO_AUTH_TOKEN')); - $to = ['+18034041123']; - $from = '+15005550006'; + $to = [getenv("TWILIO_TO")]; + $from = getenv("TWILIO_FROM"); $message = new SMS( to: $to, diff --git a/tests/e2e/SMS/VonageTest.php b/tests/e2e/SMS/VonageTest.php index 7f812759..e5ccf009 100644 --- a/tests/e2e/SMS/VonageTest.php +++ b/tests/e2e/SMS/VonageTest.php @@ -12,15 +12,15 @@ class VonageTest extends Base */ public function testSendSMS() { - $apiKey = '9bae1fbc'; - $apiSecret = 'thVmE4JpL4sPu4f6'; + $apiKey = getenv("VONAGE_API_KEY"); + $apiSecret = getenv("VONAGE_API_SECRET"); $sender = new Vonage($apiKey, $apiSecret); $message = new SMS( - to: ['+18034041123'], + to: [getenv("VONAGE_TO")], content: 'Test Content', - from: '+12082740872' + from: getenv("VONAGE_FROM") ); $result = \json_decode($sender->send($message), true); From b67db7026d32069e772bf7d080d2f73fec46e1a2 Mon Sep 17 00:00:00 2001 From: wess Date: Mon, 11 Sep 2023 11:02:07 -0400 Subject: [PATCH 28/39] Updates sendgrid to check for empty/success response --- tests/e2e/Email/SendgridTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/e2e/Email/SendgridTest.php b/tests/e2e/Email/SendgridTest.php index 05bcc3ab..b74646d6 100644 --- a/tests/e2e/Email/SendgridTest.php +++ b/tests/e2e/Email/SendgridTest.php @@ -27,8 +27,8 @@ public function testSendEmail() content: $content, ); - $sender->send($message); + $response = $sender->send($message); - $this->assertEquals(true, true); + $this->assertEquals($response, ""); } } From abb795fd85bb69f1c7532b1c3ddef8726ecf4396 Mon Sep 17 00:00:00 2001 From: wess Date: Mon, 11 Sep 2023 12:14:45 -0400 Subject: [PATCH 29/39] LINT run. Telnyx and Msg91 skipped --- src/Utopia/Messaging/Adapters/SMS/Msg91.php | 18 ++++++++++++++++++ tests/e2e/Email/SendgridTest.php | 2 +- tests/e2e/Push/APNSTest.php | 2 +- tests/e2e/Push/FCMTest.php | 2 +- tests/e2e/SMS/Msg91Test.php | 19 +++++++++++-------- tests/e2e/SMS/TwilioTest.php | 4 ++-- tests/e2e/SMS/VonageTest.php | 8 ++++---- 7 files changed, 38 insertions(+), 17 deletions(-) diff --git a/src/Utopia/Messaging/Adapters/SMS/Msg91.php b/src/Utopia/Messaging/Adapters/SMS/Msg91.php index 82b2527d..766832f8 100644 --- a/src/Utopia/Messaging/Adapters/SMS/Msg91.php +++ b/src/Utopia/Messaging/Adapters/SMS/Msg91.php @@ -58,4 +58,22 @@ protected function process(SMS $message): string ]), ); } + + private function addTemplate(string $content, string $senderId, string $templateName, string $smsType = 'NORMAL') + { + return $this->request( + method: 'POST', + url: 'https://control.msg91.com/api/v5/sms/addTemplate', + headers: [ + 'content-type: application/json', + "authkey: {$this->authKey}", + ], + body: \json_encode([ + 'template' => $content, + 'sender_id' => $senderId, + 'template_name' => $templateName, + 'smsType' => 'NORMAL', + ]) + ); + } } diff --git a/tests/e2e/Email/SendgridTest.php b/tests/e2e/Email/SendgridTest.php index b74646d6..9437b167 100644 --- a/tests/e2e/Email/SendgridTest.php +++ b/tests/e2e/Email/SendgridTest.php @@ -29,6 +29,6 @@ public function testSendEmail() $response = $sender->send($message); - $this->assertEquals($response, ""); + $this->assertEquals($response, ''); } } diff --git a/tests/e2e/Push/APNSTest.php b/tests/e2e/Push/APNSTest.php index 0be2613c..dd7152ac 100644 --- a/tests/e2e/Push/APNSTest.php +++ b/tests/e2e/Push/APNSTest.php @@ -18,7 +18,7 @@ public function testSend(): void $adapter = new APNSAdapter($authKey, $authKeyId, $teamId, $bundleId, $endpoint); $message = new Push( - to: [getenv("APNS_TO")], + to: [getenv('APNS_TO')], title: 'TestTitle', body: 'TestBody', data: null, diff --git a/tests/e2e/Push/FCMTest.php b/tests/e2e/Push/FCMTest.php index f3e86668..78538d86 100644 --- a/tests/e2e/Push/FCMTest.php +++ b/tests/e2e/Push/FCMTest.php @@ -14,7 +14,7 @@ public function testSend(): void $adapter = new FCMAdapter($serverKey); $message = new Push( - to: [getenv("FCM_SERVER_TO")], + to: [getenv('FCM_SERVER_TO')], title: 'TestTitle', body: 'TestBody', data: null, diff --git a/tests/e2e/SMS/Msg91Test.php b/tests/e2e/SMS/Msg91Test.php index 582bdf34..df90f4c4 100644 --- a/tests/e2e/SMS/Msg91Test.php +++ b/tests/e2e/SMS/Msg91Test.php @@ -12,16 +12,19 @@ class Msg91Test extends Base */ public function testSendSMS() { - $sender = new Msg91(getenv("MSG_91_SENDER_ID"), getenv("MSG_91_AUTH_KEY")); + // $sender = new Msg91(getenv('MSG_91_SENDER_ID'), getenv('MSG_91_AUTH_KEY')); - $message = new SMS( - to: [getenv("MSG_91_TO")], - content: 'Test Content', - from: getenv("MSG_91_FROM") - ); + // $message = new SMS( + // to: [getenv('MSG_91_TO')], + // content: 'Test Content', + // from: getenv('MSG_91_FROM') + // ); - $result = \json_decode($sender->send($message), true); + // $response = $sender->send($message); + // $result = \json_decode($response, true); - $this->assertEquals('success', $result['type']); + // $this->assertEquals('success', $result['type']); + + $this->markTestSkipped('Msg91 requires business verification to use template and SMS api.'); } } diff --git a/tests/e2e/SMS/TwilioTest.php b/tests/e2e/SMS/TwilioTest.php index 90fad2df..7e45d620 100644 --- a/tests/e2e/SMS/TwilioTest.php +++ b/tests/e2e/SMS/TwilioTest.php @@ -13,8 +13,8 @@ class TwilioTest extends Base public function testSendSMS() { $sender = new Twilio(getenv('TWILIO_ACCOUNT_SID'), getenv('TWILIO_AUTH_TOKEN')); - $to = [getenv("TWILIO_TO")]; - $from = getenv("TWILIO_FROM"); + $to = [getenv('TWILIO_TO')]; + $from = getenv('TWILIO_FROM'); $message = new SMS( to: $to, diff --git a/tests/e2e/SMS/VonageTest.php b/tests/e2e/SMS/VonageTest.php index e5ccf009..9e012ea3 100644 --- a/tests/e2e/SMS/VonageTest.php +++ b/tests/e2e/SMS/VonageTest.php @@ -12,15 +12,15 @@ class VonageTest extends Base */ public function testSendSMS() { - $apiKey = getenv("VONAGE_API_KEY"); - $apiSecret = getenv("VONAGE_API_SECRET"); + $apiKey = getenv('VONAGE_API_KEY'); + $apiSecret = getenv('VONAGE_API_SECRET'); $sender = new Vonage($apiKey, $apiSecret); $message = new SMS( - to: [getenv("VONAGE_TO")], + to: [getenv('VONAGE_TO')], content: 'Test Content', - from: getenv("VONAGE_FROM") + from: getenv('VONAGE_FROM') ); $result = \json_decode($sender->send($message), true); From 91885b098372142a270b429f66bdcb0bee233528 Mon Sep 17 00:00:00 2001 From: wess Date: Mon, 11 Sep 2023 12:24:33 -0400 Subject: [PATCH 30/39] Updates docker compose with vonage vars --- docker-compose.yml | 4 ++++ tests/e2e/SMS/VonageTest.php | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index 238cd4c1..e8224aaa 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -19,6 +19,10 @@ services: - MSG_91_AUTH_KEY - TEST_EMAIL - TEST_FROM_EMAIL + - VONAGE_API_KEY + - VONAGE_API_SECRET + - VONAGE_TO + - VONAGE_FROM build: context: . volumes: diff --git a/tests/e2e/SMS/VonageTest.php b/tests/e2e/SMS/VonageTest.php index 9e012ea3..02f89228 100644 --- a/tests/e2e/SMS/VonageTest.php +++ b/tests/e2e/SMS/VonageTest.php @@ -23,7 +23,9 @@ public function testSendSMS() from: getenv('VONAGE_FROM') ); - $result = \json_decode($sender->send($message), true); + $response = $sender->send($message); + + $result = \json_decode($response, true); $this->assertArrayHasKey('messages', $result); $this->assertEquals(1, count($result['messages'])); From 5114e74ed35bb81ec2c9c9949b34a8aac5d7c22f Mon Sep 17 00:00:00 2001 From: wess Date: Mon, 11 Sep 2023 12:27:09 -0400 Subject: [PATCH 31/39] Adds FCM key to env --- docker-compose.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/docker-compose.yml b/docker-compose.yml index e8224aaa..9d621bde 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,6 +7,7 @@ services: - MAILGUN_DOMAIN - SENDGRID_API_KEY - FCM_SERVER_KEY + - FCM_SERVER_TO - TWILIO_ACCOUNT_SID - TWILIO_AUTH_TOKEN - TELNYX_API_KEY From ee8c94c26439b298fac7009a4b33cb6d93d772fc Mon Sep 17 00:00:00 2001 From: wess Date: Mon, 11 Sep 2023 12:33:17 -0400 Subject: [PATCH 32/39] Adds vars to test workflow --- .github/workflows/tests.yml | 51 +++++++++++++++++++------------------ docker-compose.yml | 47 +++++++++++++++++++--------------- 2 files changed, 52 insertions(+), 46 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 86d42d4c..851daafd 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -14,31 +14,32 @@ jobs: - run: git checkout HEAD^2 - name: Run Tests env: - MAILGUN_API_KEY: ${{ secrets.MAILGUN_API_KEY }} - MAILGUN_DOMAIN: ${{ secrets.MAILGUN_DOMAIN }} - SENDGRID_API_KEY: ${{ secrets.SENDGRID_API_KEY }} - FCM_SERVER_KEY: ${{ secrets.FCM_SERVER_KEY }} - TWILIO_ACCOUNT_SID: ${{ secrets.TWILIO_ACCOUNT_SID }} - TWILIO_AUTH_TOKEN: ${{ secrets.TWILIO_AUTH_TOKEN }} - TWILIO_TO: ${{ secrets.TWILIO_TO }} - TWILIO_FROM: ${{ secrets.TWILIO_FROM }} - TELNYX_API_KEY: ${{ secrets.TELNYX_API_KEY }} - TELNYX_PUBLIC_KEY: ${{ secrets.TELNYX_PUBLIC_KEY }} - APNS_AUTHKEY_8KVVCLA3HL: ${{ secrets.APNS_AUTHKEY_8KVVCLA3HL }} - APNS_AUTH_ID: ${{ secrets.APNS_AUTH_ID }} - APNS_TEAM_ID: ${{ secrets.APNS_TEAM_ID }} - APNS_TO: ${{ secrets.APNS_TO }} - APNS_BUNDLE_ID: ${{ secrets.APNS_BUNDLE_ID }} - MSG_91_SENDER_ID: ${{ secrets.MSG_91_SENDER_ID }} - MSG_91_AUTH_KEY: ${{ secrets.MSG_91_AUTH_KEY }} - MSG_91_TO: ${{ secrets.MSG_91_TO}} - MSG_91_FROM: ${{ secrets.MSG_91_FROM }} - TEST_EMAIL: ${{ secrets.TEST_EMAIL }} - TEST_FROM_EMAIL: ${{ secrets.TEST_FROM_EMAIL }} - VONAGE_API_KEY: ${{ secrets.VONAGE_API_KEY }} - VONAGE_API_SECRET: ${{ secrets.VONAGE_API_SECRET }} - VONAGE_TO: ${{ secrets.VONAGE_TO }} - VONAGE_FROM: ${{ secrets.VONAGE_FROM }} + MAILGUN_API_KEY: $${{ secrets.MAILGUN_API_KEY }} + MAILGUN_DOMAIN: $${{ secrets.MAILGUN_DOMAIN }} + SENDGRID_API_KEY: $${{ secrets.SENDGRID_API_KEY }} + FCM_SERVER_KEY: $${{ secrets.FCM_SERVER_KEY }} + FCM_SERVER_TO: $${{ secrets.FCM_SERVER_TO }} + TWILIO_ACCOUNT_SID: $${{ secrets.TWILIO_ACCOUNT_SID }} + TWILIO_AUTH_TOKEN: $${{ secrets.TWILIO_AUTH_TOKEN }} + TWILIO_TO: $${{ secrets.TWILIO_TO }} + TWILIO_FROM: $${{ secrets.TWILIO_FROM }} + TELNYX_API_KEY: $${{ secrets.TELNYX_API_KEY }} + TELNYX_PUBLIC_KEY: $${{ secrets.TELNYX_PUBLIC_KEY }} + APNS_AUTHKEY_8KVVCLA3HL: $${{ secrets.APNS_AUTHKEY_8KVVCLA3HL }} + APNS_AUTH_ID: $${{ secrets.APNS_AUTH_ID }} + APNS_TEAM_ID: $${{ secrets.APNS_TEAM_ID }} + APNS_BUNDLE_ID: $${{ secrets.APNS_BUNDLE_ID }} + APNS_TO: $${{ secrets.APNS_TO }} + MSG_91_SENDER_ID: $${{ secrets.MSG_91_SENDER_ID }} + MSG_91_AUTH_KEY: $${{ secrets.MSG_91_AUTH_KEY }} + MSG_91_TO: $${{ secrets.MSG_91_TO }} + MSG_91_FROM: $${{ secrets.MSG_91_FROM }} + TEST_EMAIL: $${{ secrets.TEST_EMAIL }} + TEST_FROM_EMAIL: $${{ secrets.TEST_FROM_EMAIL }} + VONAGE_API_KEY: $${{ secrets.VONAGE_API_KEY }} + VONAGE_API_SECRET: $${{ secrets.VONAGE_API_SECRET }} + VONAGE_TO: $${{ secrets.VONAGE_TO }} + VONAGE_FROM: $${{ secrets.VONAGE_FROM }} run: | docker compose up -d --build sleep 5 diff --git a/docker-compose.yml b/docker-compose.yml index 9d621bde..80d626d9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,27 +3,32 @@ version: '3.9' services: tests: environment: - - MAILGUN_API_KEY - - MAILGUN_DOMAIN - - SENDGRID_API_KEY - - FCM_SERVER_KEY - - FCM_SERVER_TO - - TWILIO_ACCOUNT_SID - - TWILIO_AUTH_TOKEN - - TELNYX_API_KEY - - TELNYX_PUBLIC_KEY - - APNS_AUTHKEY_8KVVCLA3HL - - APNS_AUTH_ID - - APNS_TEAM_ID - - APNS_BUNDLE_ID - - MSG_91_SENDER_ID - - MSG_91_AUTH_KEY - - TEST_EMAIL - - TEST_FROM_EMAIL - - VONAGE_API_KEY - - VONAGE_API_SECRET - - VONAGE_TO - - VONAGE_FROM + - MAILGUN_API_KEY + - MAILGUN_DOMAIN + - SENDGRID_API_KEY + - FCM_SERVER_KEY + - FCM_SERVER_TO + - TWILIO_ACCOUNT_SID + - TWILIO_AUTH_TOKEN + - TWILIO_TO + - TWILIO_FROM + - TELNYX_API_KEY + - TELNYX_PUBLIC_KEY + - APNS_AUTHKEY_8KVVCLA3HL + - APNS_AUTH_ID + - APNS_TEAM_ID + - APNS_BUNDLE_ID + - APNS_TO + - MSG_91_SENDER_ID + - MSG_91_AUTH_KEY + - MSG_91_TO + - MSG_91_FROM + - TEST_EMAIL + - TEST_FROM_EMAIL + - VONAGE_API_KEY + - VONAGE_API_SECRET + - VONAGE_TO + - VONAGE_FROM build: context: . volumes: From a627f560716d1b1c6d86010908002e1f43fe8848 Mon Sep 17 00:00:00 2001 From: wess Date: Mon, 11 Sep 2023 12:42:00 -0400 Subject: [PATCH 33/39] removed extra character in yml --- .github/workflows/tests.yml | 54 ++++++++++++++++++------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 851daafd..552b207d 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -12,34 +12,34 @@ jobs: with: fetch-depth: 2 - run: git checkout HEAD^2 - - name: Run Tests + - name: Run Tests env: - MAILGUN_API_KEY: $${{ secrets.MAILGUN_API_KEY }} - MAILGUN_DOMAIN: $${{ secrets.MAILGUN_DOMAIN }} - SENDGRID_API_KEY: $${{ secrets.SENDGRID_API_KEY }} - FCM_SERVER_KEY: $${{ secrets.FCM_SERVER_KEY }} - FCM_SERVER_TO: $${{ secrets.FCM_SERVER_TO }} - TWILIO_ACCOUNT_SID: $${{ secrets.TWILIO_ACCOUNT_SID }} - TWILIO_AUTH_TOKEN: $${{ secrets.TWILIO_AUTH_TOKEN }} - TWILIO_TO: $${{ secrets.TWILIO_TO }} - TWILIO_FROM: $${{ secrets.TWILIO_FROM }} - TELNYX_API_KEY: $${{ secrets.TELNYX_API_KEY }} - TELNYX_PUBLIC_KEY: $${{ secrets.TELNYX_PUBLIC_KEY }} - APNS_AUTHKEY_8KVVCLA3HL: $${{ secrets.APNS_AUTHKEY_8KVVCLA3HL }} - APNS_AUTH_ID: $${{ secrets.APNS_AUTH_ID }} - APNS_TEAM_ID: $${{ secrets.APNS_TEAM_ID }} - APNS_BUNDLE_ID: $${{ secrets.APNS_BUNDLE_ID }} - APNS_TO: $${{ secrets.APNS_TO }} - MSG_91_SENDER_ID: $${{ secrets.MSG_91_SENDER_ID }} - MSG_91_AUTH_KEY: $${{ secrets.MSG_91_AUTH_KEY }} - MSG_91_TO: $${{ secrets.MSG_91_TO }} - MSG_91_FROM: $${{ secrets.MSG_91_FROM }} - TEST_EMAIL: $${{ secrets.TEST_EMAIL }} - TEST_FROM_EMAIL: $${{ secrets.TEST_FROM_EMAIL }} - VONAGE_API_KEY: $${{ secrets.VONAGE_API_KEY }} - VONAGE_API_SECRET: $${{ secrets.VONAGE_API_SECRET }} - VONAGE_TO: $${{ secrets.VONAGE_TO }} - VONAGE_FROM: $${{ secrets.VONAGE_FROM }} + MAILGUN_API_KEY: ${{ secrets.MAILGUN_API_KEY }} + MAILGUN_DOMAIN: ${{ secrets.MAILGUN_DOMAIN }} + SENDGRID_API_KEY: ${{ secrets.SENDGRID_API_KEY }} + FCM_SERVER_KEY: ${{ secrets.FCM_SERVER_KEY }} + FCM_SERVER_TO: ${{ secrets.FCM_SERVER_TO }} + TWILIO_ACCOUNT_SID: ${{ secrets.TWILIO_ACCOUNT_SID }} + TWILIO_AUTH_TOKEN: ${{ secrets.TWILIO_AUTH_TOKEN }} + TWILIO_TO: ${{ secrets.TWILIO_TO }} + TWILIO_FROM: ${{ secrets.TWILIO_FROM }} + TELNYX_API_KEY: ${{ secrets.TELNYX_API_KEY }} + TELNYX_PUBLIC_KEY: ${{ secrets.TELNYX_PUBLIC_KEY }} + APNS_AUTHKEY_8KVVCLA3HL: ${{ secrets.APNS_AUTHKEY_8KVVCLA3HL }} + APNS_AUTH_ID: ${{ secrets.APNS_AUTH_ID }} + APNS_TEAM_ID: ${{ secrets.APNS_TEAM_ID }} + APNS_BUNDLE_ID: ${{ secrets.APNS_BUNDLE_ID }} + APNS_TO: ${{ secrets.APNS_TO }} + MSG_91_SENDER_ID: ${{ secrets.MSG_91_SENDER_ID }} + MSG_91_AUTH_KEY: ${{ secrets.MSG_91_AUTH_KEY }} + MSG_91_TO: ${{ secrets.MSG_91_TO }} + MSG_91_FROM: ${{ secrets.MSG_91_FROM }} + TEST_EMAIL: ${{ secrets.TEST_EMAIL }} + TEST_FROM_EMAIL: ${{ secrets.TEST_FROM_EMAIL }} + VONAGE_API_KEY: ${{ secrets.VONAGE_API_KEY }} + VONAGE_API_SECRET: ${{ secrets.VONAGE_API_SECRET }} + VONAGE_TO: ${{ secrets.VONAGE_TO }} + VONAGE_FROM: ${{ secrets.VONAGE_FROM }} run: | docker compose up -d --build sleep 5 From 2d7001fb2ee514bfd7997df1f3a629fb08ec2420 Mon Sep 17 00:00:00 2001 From: wess Date: Mon, 11 Sep 2023 12:45:34 -0400 Subject: [PATCH 34/39] Restart job --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 445406d1..09fef071 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Install using composer: composer require utopia-php/messaging ``` -## Email +## Email ```php Date: Mon, 11 Sep 2023 13:05:36 -0400 Subject: [PATCH 35/39] testing with inline value --- tests/e2e/Push/FCMTest.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/e2e/Push/FCMTest.php b/tests/e2e/Push/FCMTest.php index 78538d86..d2c97fe6 100644 --- a/tests/e2e/Push/FCMTest.php +++ b/tests/e2e/Push/FCMTest.php @@ -13,8 +13,10 @@ public function testSend(): void $adapter = new FCMAdapter($serverKey); + $to = "eJa9AhokQUudfBPJwRx2OX:APA91bE0KbMkXU7a4eCyq1CyN1nR9TwOD5NQIaHADJBMBV1GjOjTfyPywOXKVeKVvvjz6nvB2jASGtRxGJHsM4Z4osoHnTx5IrnxCNUDEH11wsm4vMBiKW0zbugVis1MdtusTu9admrk"; //getenv('FCM_SERVER_TO'); + $message = new Push( - to: [getenv('FCM_SERVER_TO')], + to: [$to], title: 'TestTitle', body: 'TestBody', data: null, From a9e900002c94da7ec7fb7b3967b6b59238c4e6b2 Mon Sep 17 00:00:00 2001 From: wess Date: Mon, 11 Sep 2023 13:07:26 -0400 Subject: [PATCH 36/39] Adds FCM_SERVER_TO back in to ENV --- tests/e2e/Push/FCMTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e/Push/FCMTest.php b/tests/e2e/Push/FCMTest.php index d2c97fe6..f7befabe 100644 --- a/tests/e2e/Push/FCMTest.php +++ b/tests/e2e/Push/FCMTest.php @@ -13,7 +13,7 @@ public function testSend(): void $adapter = new FCMAdapter($serverKey); - $to = "eJa9AhokQUudfBPJwRx2OX:APA91bE0KbMkXU7a4eCyq1CyN1nR9TwOD5NQIaHADJBMBV1GjOjTfyPywOXKVeKVvvjz6nvB2jASGtRxGJHsM4Z4osoHnTx5IrnxCNUDEH11wsm4vMBiKW0zbugVis1MdtusTu9admrk"; //getenv('FCM_SERVER_TO'); + $to = getenv('FCM_SERVER_TO'); $message = new Push( to: [$to], From 6367acc3329f31c59fc3326db15ce914a3d4c0f4 Mon Sep 17 00:00:00 2001 From: wess Date: Mon, 11 Sep 2023 17:04:17 -0400 Subject: [PATCH 37/39] Lint and changes --- src/Utopia/Messaging/Adapters/Email/Mailgun.php | 4 ++-- src/Utopia/Messaging/Adapters/Email/Sendgrid.php | 1 - src/Utopia/Messaging/Adapters/Push/APNS.php | 6 ++++++ tests/e2e/Email/MailgunTest.php | 2 +- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/Utopia/Messaging/Adapters/Email/Mailgun.php b/src/Utopia/Messaging/Adapters/Email/Mailgun.php index 21c70118..4e3882e8 100644 --- a/src/Utopia/Messaging/Adapters/Email/Mailgun.php +++ b/src/Utopia/Messaging/Adapters/Email/Mailgun.php @@ -14,7 +14,7 @@ class Mailgun extends EmailAdapter public function __construct( private string $apiKey, private string $domain, - private bool $isUS = true + private bool $isEU = false ) { } @@ -48,7 +48,7 @@ protected function process(Email $message): string $usDomain = 'api.mailgun.net'; $euDomain = 'api.eu.mailgun.net'; - $domain = $this->isUS ? $usDomain : $euDomain; + $domain = $this->isEU ? $euDomain : $usDomain; $response = $this->request( method: 'POST', diff --git a/src/Utopia/Messaging/Adapters/Email/Sendgrid.php b/src/Utopia/Messaging/Adapters/Email/Sendgrid.php index 28dc661b..e91c31cc 100644 --- a/src/Utopia/Messaging/Adapters/Email/Sendgrid.php +++ b/src/Utopia/Messaging/Adapters/Email/Sendgrid.php @@ -2,7 +2,6 @@ namespace Utopia\Messaging\Adapters\Email; -use Exception; use Utopia\Messaging\Adapters\Email as EmailAdapter; use Utopia\Messaging\Messages\Email; diff --git a/src/Utopia/Messaging/Adapters/Push/APNS.php b/src/Utopia/Messaging/Adapters/Push/APNS.php index ed89c4da..40658e32 100644 --- a/src/Utopia/Messaging/Adapters/Push/APNS.php +++ b/src/Utopia/Messaging/Adapters/Push/APNS.php @@ -75,10 +75,16 @@ private function notify(array $to, array $payload): array $headers = [ 'authorization: bearer '.$this->generateJwt(), 'apns-topic: '.$this->bundleId, + 'apns-push-type: alert', ]; + $sh = curl_share_init(); + + curl_share_setopt($sh, CURLSHOPT_SHARE, CURL_LOCK_DATA_CONNECT); + $ch = curl_init(); + curl_setopt($ch, CURLOPT_SHARE, $sh); curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0); curl_setopt_array($ch, [ diff --git a/tests/e2e/Email/MailgunTest.php b/tests/e2e/Email/MailgunTest.php index 0a6f018f..fed2489d 100644 --- a/tests/e2e/Email/MailgunTest.php +++ b/tests/e2e/Email/MailgunTest.php @@ -18,7 +18,7 @@ public function testSendEmail() $sender = new Mailgun( apiKey: $key, domain: $domain, - isUS: true + isEU: false ); $to = getenv('TEST_EMAIL'); From fcde4b47a1cb0477cf24c0825b3334a214885813 Mon Sep 17 00:00:00 2001 From: wess Date: Mon, 11 Sep 2023 19:16:52 -0400 Subject: [PATCH 38/39] APNS max send bumped to 5k --- src/Utopia/Messaging/Adapters/Push/APNS.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Utopia/Messaging/Adapters/Push/APNS.php b/src/Utopia/Messaging/Adapters/Push/APNS.php index 40658e32..75d94dcc 100644 --- a/src/Utopia/Messaging/Adapters/Push/APNS.php +++ b/src/Utopia/Messaging/Adapters/Push/APNS.php @@ -42,7 +42,7 @@ public function getName(): string */ public function getMaxMessagesPerRequest(): int { - return 1000; + return 5000; } /** From 2bb09220d0993a9f8f0afc63ff51382b13d93e18 Mon Sep 17 00:00:00 2001 From: wess Date: Thu, 14 Sep 2023 16:29:49 -0400 Subject: [PATCH 39/39] Uses curl_multi for apple push --- src/Utopia/Messaging/Adapters/Push/APNS.php | 46 +++++++++++++++++++-- tests/e2e/Push/APNSTest.php | 8 ++-- 2 files changed, 47 insertions(+), 7 deletions(-) diff --git a/src/Utopia/Messaging/Adapters/Push/APNS.php b/src/Utopia/Messaging/Adapters/Push/APNS.php index 75d94dcc..56282d83 100644 --- a/src/Utopia/Messaging/Adapters/Push/APNS.php +++ b/src/Utopia/Messaging/Adapters/Push/APNS.php @@ -99,15 +99,55 @@ private function notify(array $to, array $payload): array $response = ''; + $mh = curl_multi_init(); + $handles = []; + + // Create a handle for each request foreach ($to as $token) { curl_setopt($ch, CURLOPT_URL, $this->endpoint.'/3/device/'.$token); - $response = curl_exec($ch); + $handle = curl_copy_handle($ch); + curl_multi_add_handle($mh, $handle); + + $handles[] = $handle; + } + + $active = null; + $status = CURLM_OK; + + // Execute the handles + while ($active && $status == CURLM_OK) { + $status = curl_multi_exec($mh, $active); + } + + // Check each handle's result + $responses = []; + foreach ($handles as $ch) { + $urlInfo = curl_getinfo($ch); + $device = basename($urlInfo['url']); // Extracts deviceToken from the URL + + if (! curl_errno($ch)) { + $statusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + $responses[] = [ + 'device' => $device, + 'status' => 'success', + 'statusCode' => $statusCode, + ]; + } else { + $responses[$device] = [ + 'status' => 'error', + 'error' => curl_error($ch), + ]; + } + + curl_multi_remove_handle($mh, $ch); + curl_close($ch); } - curl_close($ch); + curl_multi_close($mh); + curl_share_close($sh); - return $this->formatResponse($response); + return $responses; } private function formatResponse(string $response): array diff --git a/tests/e2e/Push/APNSTest.php b/tests/e2e/Push/APNSTest.php index dd7152ac..cbd1a354 100644 --- a/tests/e2e/Push/APNSTest.php +++ b/tests/e2e/Push/APNSTest.php @@ -30,10 +30,10 @@ public function testSend(): void badge: '1' ); - $response = (array) \json_decode($adapter->send($message)); + $responses = \json_decode($adapter->send($message)); - $this->assertEquals('200', $response['status']); - $this->assertArrayHasKey('apns-id', $response); - $this->assertArrayHasKey('apns-unique-id', $response); + foreach ($responses as $response) { + $this->assertEquals('success', $response->status); + } } }