From 616da2deb4b1daaab7409660cf629d8235f6985d Mon Sep 17 00:00:00 2001 From: Biswajit Mondal Date: Thu, 5 Oct 2023 00:24:53 +0530 Subject: [PATCH 1/3] Added SmsGlobal messaging adapter --- .github/workflows/tests.yml | 4 + docker-compose.yml | 4 + .../Messaging/Adapters/SMS/SmsGlobal.php | 146 ++++++++++++++++++ tests/e2e/SMS/SmsGlobalTest.php | 36 +++++ 4 files changed, 190 insertions(+) create mode 100644 src/Utopia/Messaging/Adapters/SMS/SmsGlobal.php create mode 100644 tests/e2e/SMS/SmsGlobalTest.php diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 552b207d..25516184 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -40,6 +40,10 @@ jobs: VONAGE_API_SECRET: ${{ secrets.VONAGE_API_SECRET }} VONAGE_TO: ${{ secrets.VONAGE_TO }} VONAGE_FROM: ${{ secrets.VONAGE_FROM }} + SMS_GLOBAL_API_KEY: ${{ secrets.SMS_GLOBAL_API_KEY }} + SMS_GLOBAL_API_SECRET: ${{ secrets.SMS_GLOBAL_API_SECRET }} + SMS_GLOBAL_TO: ${{ secrets.SMS_GLOBAL_TO }} + SMS_GLOBAL_FROM: ${{ secrets.SMS_GLOBAL_FROM }} run: | docker compose up -d --build sleep 5 diff --git a/docker-compose.yml b/docker-compose.yml index 80d626d9..d6c9d2cd 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -29,6 +29,10 @@ services: - VONAGE_API_SECRET - VONAGE_TO - VONAGE_FROM + - SMS_GLOBAL_API_KEY + - SMS_GLOBAL_API_SECRET + - SMS_GLOBAL_TO + - SMS_GLOBAL_FROM build: context: . volumes: diff --git a/src/Utopia/Messaging/Adapters/SMS/SmsGlobal.php b/src/Utopia/Messaging/Adapters/SMS/SmsGlobal.php new file mode 100644 index 00000000..12add879 --- /dev/null +++ b/src/Utopia/Messaging/Adapters/SMS/SmsGlobal.php @@ -0,0 +1,146 @@ +getAuthorizationHeader( + method: 'POST', + requestUri: '/v2/sms/', + host: 'api.smsglobal.com' + ); + + return $this->request( + method: 'POST', + url: "https://api.smsglobal.com/v2/sms/", + headers: [ + 'Authorization: ' . $authorizationHeader, + 'Content-Type: application/json', + ], + body: \json_encode( + $this->getRequestBody( + to: $message->getTo(), + text: $message->getContent(), + from: $message->getFrom() + ) + ), + ); + } + + /** + * Get the value to use for the Authorization header + * + * @param string $method HTTP method (e.g. POST) + * @param string $requestUri Request URI (e.g. /v2/sms/) + * @param string $host Hostname + * @return string + */ + public function getAuthorizationHeader(string $method, string $requestUri, string $host): string + { + // Server or computer time should match with the current Unix timestamp otherwise authentication will fail + $timestamp = time(); + $nonce = md5(microtime() . mt_rand()); + + $hash = $this->getRequestHash( + timestamp: $timestamp, + nonce: $nonce, + method: $method, + requestUri: $requestUri, + host: $host + ); + + $headerFormat = 'MAC id="%s", ts="%s", nonce="%s", mac="%s"'; + $header = sprintf($headerFormat, $this->apiKey, $timestamp, $nonce, $hash); + return $header; + } + + /** + * Hashes a request using the API secret, to use in the Authorization header + * + * @param int $timestamp Unix timestamp of request time + * @param string $nonce Random unique string + * @param string $method HTTP method (e.g. POST) + * @param string $requestUri Request URI (e.g. /v1/sms/) + * @param string $host Hostname + * @param int $port Port (e.g. 443) + * @return string + */ + private function getRequestHash( + int $timestamp, + string $nonce, + string $method, + string $requestUri, + string $host, + int $port = 443 + ) { + $string = array($timestamp, $nonce, $method, $requestUri, $host, $port, ''); + $string = sprintf("%s\n", implode("\n", $string)); + $hash = hash_hmac(self::HASH_ALGO, $string, $this->apiSecret, true); + $hash = base64_encode($hash); + return $hash; + } + + /** + * Get the request body + * + * @param array $to + * @param string $text + * @param string|null $from + * @return array + */ + private function getRequestBody(array $to, string $text, string $from = null): array + { + $origin = !empty($from) ? $from : ''; + if (count($to) == 1) { + return [ + "destination" => $to[0], + "message" => $text, + "origin" => $origin, + ]; + } else { + return [ + "destinations" => $to, + "message" => $text, + "origin" => $origin, + ]; + } + } +} diff --git a/tests/e2e/SMS/SmsGlobalTest.php b/tests/e2e/SMS/SmsGlobalTest.php new file mode 100644 index 00000000..8b55f1ba --- /dev/null +++ b/tests/e2e/SMS/SmsGlobalTest.php @@ -0,0 +1,36 @@ +send($message); + $result = \json_decode($response, true); + + $this->assertArrayHasKey('messages', $result); + $this->assertEquals(count($to), count($result['messages'])); + + // $dummyResponseStructure = '{"messages":[{"id":"154","outgoing_id":1,"origin":"origin","destination":"destination","message":"Test Content","status":"sent","dateTime":""}]}'; + } +} From 8131f43a7caf2f9c63bdc5aca889f68c14597366 Mon Sep 17 00:00:00 2001 From: Biswajit Mondal Date: Thu, 5 Oct 2023 21:18:26 +0530 Subject: [PATCH 2/3] fixed linting issues and removed unnecessary comment --- .../Messaging/Adapters/SMS/SmsGlobal.php | 64 ++++++++++--------- tests/e2e/SMS/SmsGlobalTest.php | 4 +- 2 files changed, 35 insertions(+), 33 deletions(-) diff --git a/src/Utopia/Messaging/Adapters/SMS/SmsGlobal.php b/src/Utopia/Messaging/Adapters/SMS/SmsGlobal.php index 12add879..af93ff59 100644 --- a/src/Utopia/Messaging/Adapters/SMS/SmsGlobal.php +++ b/src/Utopia/Messaging/Adapters/SMS/SmsGlobal.php @@ -50,9 +50,9 @@ protected function process(SMS $message): string return $this->request( method: 'POST', - url: "https://api.smsglobal.com/v2/sms/", + url: 'https://api.smsglobal.com/v2/sms/', headers: [ - 'Authorization: ' . $authorizationHeader, + 'Authorization: '.$authorizationHeader, 'Content-Type: application/json', ], body: \json_encode( @@ -68,16 +68,20 @@ protected function process(SMS $message): string /** * Get the value to use for the Authorization header * - * @param string $method HTTP method (e.g. POST) - * @param string $requestUri Request URI (e.g. /v2/sms/) - * @param string $host Hostname - * @return string + * @param string $method HTTP method (e.g. POST) + * @param string $requestUri Request URI (e.g. /v2/sms/) + * @param string $host Hostname */ - public function getAuthorizationHeader(string $method, string $requestUri, string $host): string - { - // Server or computer time should match with the current Unix timestamp otherwise authentication will fail + public function getAuthorizationHeader( + string $method, + string $requestUri, + string $host + ): string { + // Server or computer time should match with the + //current Unix timestamp otherwise authentication will fail + $timestamp = time(); - $nonce = md5(microtime() . mt_rand()); + $nonce = md5(microtime().mt_rand()); $hash = $this->getRequestHash( timestamp: $timestamp, @@ -89,19 +93,19 @@ public function getAuthorizationHeader(string $method, string $requestUri, strin $headerFormat = 'MAC id="%s", ts="%s", nonce="%s", mac="%s"'; $header = sprintf($headerFormat, $this->apiKey, $timestamp, $nonce, $hash); + return $header; } /** * Hashes a request using the API secret, to use in the Authorization header * - * @param int $timestamp Unix timestamp of request time - * @param string $nonce Random unique string - * @param string $method HTTP method (e.g. POST) - * @param string $requestUri Request URI (e.g. /v1/sms/) - * @param string $host Hostname - * @param int $port Port (e.g. 443) - * @return string + * @param int $timestamp Unix timestamp of request time + * @param string $nonce Random unique string + * @param string $method HTTP method (e.g. POST) + * @param string $requestUri Request URI (e.g. /v1/sms/) + * @param string $host Hostname + * @param int $port Port (e.g. 443) */ private function getRequestHash( int $timestamp, @@ -111,35 +115,35 @@ private function getRequestHash( string $host, int $port = 443 ) { - $string = array($timestamp, $nonce, $method, $requestUri, $host, $port, ''); + $string = [$timestamp, $nonce, $method, $requestUri, $host, $port, '']; $string = sprintf("%s\n", implode("\n", $string)); $hash = hash_hmac(self::HASH_ALGO, $string, $this->apiSecret, true); $hash = base64_encode($hash); + return $hash; } /** * Get the request body - * - * @param array $to - * @param string $text - * @param string|null $from - * @return array + * + * @param array $to Phone number + * @param string $text Message to send + * @param string|null $from Origin of the message */ private function getRequestBody(array $to, string $text, string $from = null): array { - $origin = !empty($from) ? $from : ''; + $origin = ! empty($from) ? $from : ''; if (count($to) == 1) { return [ - "destination" => $to[0], - "message" => $text, - "origin" => $origin, + 'destination' => $to[0], + 'message' => $text, + 'origin' => $origin, ]; } else { return [ - "destinations" => $to, - "message" => $text, - "origin" => $origin, + 'destinations' => $to, + 'message' => $text, + 'origin' => $origin, ]; } } diff --git a/tests/e2e/SMS/SmsGlobalTest.php b/tests/e2e/SMS/SmsGlobalTest.php index 8b55f1ba..9911c635 100644 --- a/tests/e2e/SMS/SmsGlobalTest.php +++ b/tests/e2e/SMS/SmsGlobalTest.php @@ -17,7 +17,7 @@ public function testSendSMS() $to = [getenv('SMS_GLOBAL_TO')]; $from = getenv('SMS_GLOBAL_FROM'); - + $sender = new SmsGlobal($apiKey, $apiSecret); $message = new SMS( to: $to, @@ -30,7 +30,5 @@ public function testSendSMS() $this->assertArrayHasKey('messages', $result); $this->assertEquals(count($to), count($result['messages'])); - - // $dummyResponseStructure = '{"messages":[{"id":"154","outgoing_id":1,"origin":"origin","destination":"destination","message":"Test Content","status":"sent","dateTime":""}]}'; } } From 67e02020598d99eb6271164f4e7a3b5463c64578 Mon Sep 17 00:00:00 2001 From: Biswajit Mondal Date: Mon, 23 Oct 2023 11:54:09 +0530 Subject: [PATCH 3/3] added null coalescing operator for checking null value --- src/Utopia/Messaging/Adapters/SMS/SmsGlobal.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Utopia/Messaging/Adapters/SMS/SmsGlobal.php b/src/Utopia/Messaging/Adapters/SMS/SmsGlobal.php index af93ff59..dbb93275 100644 --- a/src/Utopia/Messaging/Adapters/SMS/SmsGlobal.php +++ b/src/Utopia/Messaging/Adapters/SMS/SmsGlobal.php @@ -132,7 +132,7 @@ private function getRequestHash( */ private function getRequestBody(array $to, string $text, string $from = null): array { - $origin = ! empty($from) ? $from : ''; + $origin = $from ?? ''; if (count($to) == 1) { return [ 'destination' => $to[0],