diff --git a/config/textify.php b/config/textify.php index b453bfd..b8cc40a 100644 --- a/config/textify.php +++ b/config/textify.php @@ -207,4 +207,27 @@ 'events' => [ 'enabled' => env('TEXTIFY_EVENTS_ENABLED', true), ], + + /* + |-------------------------------------------------------------------------- + | Message Validation + |-------------------------------------------------------------------------- + | + | Configure validation rules for SMS messages before sending. + | Uses Laravel-style validation rules for consistency. + | + */ + + 'validation' => [ + 'message' => [ + // Set to false to allow empty messages + 'required' => env('TEXTIFY_MESSAGE_REQUIRED', true), + + // Minimum message length + 'min' => env('TEXTIFY_MESSAGE_MIN_LENGTH', 1), + + // Maximum message length (null = no limit) + 'max' => env('TEXTIFY_MESSAGE_MAX_LENGTH', null), + ], + ], ]; diff --git a/src/Providers/Bangladeshi/BulkSmsBdProvider.php b/src/Providers/Bangladeshi/BulkSmsBdProvider.php index 00c7387..43a6021 100644 --- a/src/Providers/Bangladeshi/BulkSmsBdProvider.php +++ b/src/Providers/Bangladeshi/BulkSmsBdProvider.php @@ -119,7 +119,6 @@ protected function parseResponse(array $response): TextifyResponse '1006' => 'Balance Validity Not Available', '1007' => 'Balance Insufficient', '1011' => 'User Id not found', - // Add more error codes as needed ]; $errorMessage = $errorMessages[$errorCode] ?? $responseText; } diff --git a/src/Providers/BaseProvider.php b/src/Providers/BaseProvider.php index c93d1ce..f8a7a88 100644 --- a/src/Providers/BaseProvider.php +++ b/src/Providers/BaseProvider.php @@ -132,6 +132,15 @@ public function send(TextifyMessage $message): TextifyResponse ); } + // Validate message content + $messageValidationResult = $this->validateMessageContent($message->message); + if (! $messageValidationResult['valid']) { + return TextifyResponse::failed( + errorMessage: $messageValidationResult['error'], + errorCode: 'INVALID_MESSAGE_CONTENT' + ); + } + // Format phone number $formattedMessage = new TextifyMessage( to: $this->formatPhoneNumber($message->to), @@ -292,4 +301,73 @@ protected function ensureConfigKeys(): void ); } } + + /** + * Validate message content according to configuration rules + * + * @param string $message The message content to validate + * @return array{valid: bool, error?: string} + */ + protected function validateMessageContent(string $message): array + { + // Use Laravel-style validation config structure + $required = config('textify.validation.message.required', true); + $minLength = config('textify.validation.message.min', 1); + $maxLength = config('textify.validation.message.max', null); + + $trimmedMessage = trim($message); + + // Check if message is required but empty + if ($required && empty($trimmedMessage)) { + return [ + 'valid' => false, + 'error' => 'The message field is required.', + ]; + } + + // Skip length validation if message is empty and not required + if (! $required && empty($trimmedMessage)) { + return ['valid' => true]; + } + + // Check minimum length (combines empty check with min length) + if (strlen($trimmedMessage) < $minLength) { + return [ + 'valid' => false, + 'error' => sprintf('The message must be at least %d character%s.', $minLength, $minLength === 1 ? '' : 's'), + ]; + } + + // Check maximum length if specified + if ($maxLength !== null && strlen($trimmedMessage) > $maxLength) { + return [ + 'valid' => false, + 'error' => sprintf('The message may not be greater than %d characters.', $maxLength), + ]; + } + + return ['valid' => true]; + } + + /** + * {@inheritdoc} + */ + public function validatePhoneNumber(string $phoneNumber): bool + { + // Basic validation - check if not empty and contains only digits, +, -, spaces, and parentheses + $cleaned = preg_replace('/[\s\-\(\)]+/', '', $phoneNumber); + + return ! empty(trim($phoneNumber)) && + preg_match('/^\+?[0-9]{7,15}$/', $cleaned); + } + + /** + * {@inheritdoc} + */ + public function formatPhoneNumber(string $phoneNumber): string + { + // Default implementation - just trim whitespace + // Providers should override this method for their specific formatting requirements + return trim($phoneNumber); + } } diff --git a/tests/MessageValidationTest.php b/tests/MessageValidationTest.php new file mode 100644 index 0000000..9036dbd --- /dev/null +++ b/tests/MessageValidationTest.php @@ -0,0 +1,86 @@ +send($message); + + expect($response->isSuccessful())->toBeFalse(); + expect($response->getErrorCode())->toBe('INVALID_MESSAGE_CONTENT'); + expect($response->getErrorMessage())->toContain('field is required'); +}); + +it('accepts empty messages when not required (nullable)', function () { + // Temporarily override config to make message not required (nullable) + config(['textify.validation.message.required' => false]); + + $provider = new LogProvider([]); + + $message = TextifyMessage::create('01712345678', '', 'TestSender'); + $response = $provider->send($message); + + expect($response->isSuccessful())->toBeTrue(); + + // Reset config + config(['textify.validation.message.required' => true]); +}); + +it('rejects messages shorter than minimum length', function () { + config(['textify.validation.message.min' => 5]); + + $provider = new LogProvider([]); + + $message = TextifyMessage::create('01712345678', 'Hi', 'TestSender'); + $response = $provider->send($message); + + expect($response->isSuccessful())->toBeFalse(); + expect($response->getErrorCode())->toBe('INVALID_MESSAGE_CONTENT'); + expect($response->getErrorMessage())->toContain('must be at least 5 character'); + + // Reset config + config(['textify.validation.message.min' => 1]); +}); + +it('rejects messages longer than maximum length', function () { + config(['textify.validation.message.max' => 10]); + + $provider = new LogProvider([]); + + $message = TextifyMessage::create('01712345678', 'This message is way too long', 'TestSender'); + $response = $provider->send($message); + + expect($response->isSuccessful())->toBeFalse(); + expect($response->getErrorCode())->toBe('INVALID_MESSAGE_CONTENT'); + expect($response->getErrorMessage())->toContain('may not be greater than 10 characters'); + + // Reset config + config(['textify.validation.message.max' => null]); +}); + +it('accepts valid messages', function () { + $provider = new LogProvider([]); + + $message = TextifyMessage::create('01712345678', 'Valid message content', 'TestSender'); + $response = $provider->send($message); + + expect($response->isSuccessful())->toBeTrue(); +}); + +it('validates whitespace-only messages as empty', function () { + $provider = new LogProvider([]); + + $message = TextifyMessage::create('01712345678', ' ', 'TestSender'); + $response = $provider->send($message); + + expect($response->isSuccessful())->toBeFalse(); + expect($response->getErrorCode())->toBe('INVALID_MESSAGE_CONTENT'); + expect($response->getErrorMessage())->toContain('field is required'); +});