Skip to content

Commit 7cebeaf

Browse files
Merge pull request #6 from DevWizardHQ/feature/message-validation
feat: Add comprehensive message validation with configurable Laravel-style validation rules
2 parents 2359583 + c198a1a commit 7cebeaf

File tree

4 files changed

+187
-1
lines changed

4 files changed

+187
-1
lines changed

config/textify.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,4 +207,27 @@
207207
'events' => [
208208
'enabled' => env('TEXTIFY_EVENTS_ENABLED', true),
209209
],
210+
211+
/*
212+
|--------------------------------------------------------------------------
213+
| Message Validation
214+
|--------------------------------------------------------------------------
215+
|
216+
| Configure validation rules for SMS messages before sending.
217+
| Uses Laravel-style validation rules for consistency.
218+
|
219+
*/
220+
221+
'validation' => [
222+
'message' => [
223+
// Set to false to allow empty messages
224+
'required' => env('TEXTIFY_MESSAGE_REQUIRED', true),
225+
226+
// Minimum message length
227+
'min' => env('TEXTIFY_MESSAGE_MIN_LENGTH', 1),
228+
229+
// Maximum message length (null = no limit)
230+
'max' => env('TEXTIFY_MESSAGE_MAX_LENGTH', null),
231+
],
232+
],
210233
];

src/Providers/Bangladeshi/BulkSmsBdProvider.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,6 @@ protected function parseResponse(array $response): TextifyResponse
119119
'1006' => 'Balance Validity Not Available',
120120
'1007' => 'Balance Insufficient',
121121
'1011' => 'User Id not found',
122-
// Add more error codes as needed
123122
];
124123
$errorMessage = $errorMessages[$errorCode] ?? $responseText;
125124
}

src/Providers/BaseProvider.php

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,15 @@ public function send(TextifyMessage $message): TextifyResponse
132132
);
133133
}
134134

135+
// Validate message content
136+
$messageValidationResult = $this->validateMessageContent($message->message);
137+
if (! $messageValidationResult['valid']) {
138+
return TextifyResponse::failed(
139+
errorMessage: $messageValidationResult['error'],
140+
errorCode: 'INVALID_MESSAGE_CONTENT'
141+
);
142+
}
143+
135144
// Format phone number
136145
$formattedMessage = new TextifyMessage(
137146
to: $this->formatPhoneNumber($message->to),
@@ -292,4 +301,73 @@ protected function ensureConfigKeys(): void
292301
);
293302
}
294303
}
304+
305+
/**
306+
* Validate message content according to configuration rules
307+
*
308+
* @param string $message The message content to validate
309+
* @return array{valid: bool, error?: string}
310+
*/
311+
protected function validateMessageContent(string $message): array
312+
{
313+
// Use Laravel-style validation config structure
314+
$required = config('textify.validation.message.required', true);
315+
$minLength = config('textify.validation.message.min', 1);
316+
$maxLength = config('textify.validation.message.max', null);
317+
318+
$trimmedMessage = trim($message);
319+
320+
// Check if message is required but empty
321+
if ($required && empty($trimmedMessage)) {
322+
return [
323+
'valid' => false,
324+
'error' => 'The message field is required.',
325+
];
326+
}
327+
328+
// Skip length validation if message is empty and not required
329+
if (! $required && empty($trimmedMessage)) {
330+
return ['valid' => true];
331+
}
332+
333+
// Check minimum length (combines empty check with min length)
334+
if (strlen($trimmedMessage) < $minLength) {
335+
return [
336+
'valid' => false,
337+
'error' => sprintf('The message must be at least %d character%s.', $minLength, $minLength === 1 ? '' : 's'),
338+
];
339+
}
340+
341+
// Check maximum length if specified
342+
if ($maxLength !== null && strlen($trimmedMessage) > $maxLength) {
343+
return [
344+
'valid' => false,
345+
'error' => sprintf('The message may not be greater than %d characters.', $maxLength),
346+
];
347+
}
348+
349+
return ['valid' => true];
350+
}
351+
352+
/**
353+
* {@inheritdoc}
354+
*/
355+
public function validatePhoneNumber(string $phoneNumber): bool
356+
{
357+
// Basic validation - check if not empty and contains only digits, +, -, spaces, and parentheses
358+
$cleaned = preg_replace('/[\s\-\(\)]+/', '', $phoneNumber);
359+
360+
return ! empty(trim($phoneNumber)) &&
361+
preg_match('/^\+?[0-9]{7,15}$/', $cleaned);
362+
}
363+
364+
/**
365+
* {@inheritdoc}
366+
*/
367+
public function formatPhoneNumber(string $phoneNumber): string
368+
{
369+
// Default implementation - just trim whitespace
370+
// Providers should override this method for their specific formatting requirements
371+
return trim($phoneNumber);
372+
}
295373
}

tests/MessageValidationTest.php

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace DevWizard\Textify\Tests;
6+
7+
use DevWizard\Textify\DTOs\TextifyMessage;
8+
use DevWizard\Textify\Providers\LogProvider;
9+
10+
it('rejects empty messages by default (required)', function () {
11+
$provider = new LogProvider([]);
12+
13+
$message = TextifyMessage::create('01712345678', '', 'TestSender');
14+
$response = $provider->send($message);
15+
16+
expect($response->isSuccessful())->toBeFalse();
17+
expect($response->getErrorCode())->toBe('INVALID_MESSAGE_CONTENT');
18+
expect($response->getErrorMessage())->toContain('field is required');
19+
});
20+
21+
it('accepts empty messages when not required (nullable)', function () {
22+
// Temporarily override config to make message not required (nullable)
23+
config(['textify.validation.message.required' => false]);
24+
25+
$provider = new LogProvider([]);
26+
27+
$message = TextifyMessage::create('01712345678', '', 'TestSender');
28+
$response = $provider->send($message);
29+
30+
expect($response->isSuccessful())->toBeTrue();
31+
32+
// Reset config
33+
config(['textify.validation.message.required' => true]);
34+
});
35+
36+
it('rejects messages shorter than minimum length', function () {
37+
config(['textify.validation.message.min' => 5]);
38+
39+
$provider = new LogProvider([]);
40+
41+
$message = TextifyMessage::create('01712345678', 'Hi', 'TestSender');
42+
$response = $provider->send($message);
43+
44+
expect($response->isSuccessful())->toBeFalse();
45+
expect($response->getErrorCode())->toBe('INVALID_MESSAGE_CONTENT');
46+
expect($response->getErrorMessage())->toContain('must be at least 5 character');
47+
48+
// Reset config
49+
config(['textify.validation.message.min' => 1]);
50+
});
51+
52+
it('rejects messages longer than maximum length', function () {
53+
config(['textify.validation.message.max' => 10]);
54+
55+
$provider = new LogProvider([]);
56+
57+
$message = TextifyMessage::create('01712345678', 'This message is way too long', 'TestSender');
58+
$response = $provider->send($message);
59+
60+
expect($response->isSuccessful())->toBeFalse();
61+
expect($response->getErrorCode())->toBe('INVALID_MESSAGE_CONTENT');
62+
expect($response->getErrorMessage())->toContain('may not be greater than 10 characters');
63+
64+
// Reset config
65+
config(['textify.validation.message.max' => null]);
66+
});
67+
68+
it('accepts valid messages', function () {
69+
$provider = new LogProvider([]);
70+
71+
$message = TextifyMessage::create('01712345678', 'Valid message content', 'TestSender');
72+
$response = $provider->send($message);
73+
74+
expect($response->isSuccessful())->toBeTrue();
75+
});
76+
77+
it('validates whitespace-only messages as empty', function () {
78+
$provider = new LogProvider([]);
79+
80+
$message = TextifyMessage::create('01712345678', ' ', 'TestSender');
81+
$response = $provider->send($message);
82+
83+
expect($response->isSuccessful())->toBeFalse();
84+
expect($response->getErrorCode())->toBe('INVALID_MESSAGE_CONTENT');
85+
expect($response->getErrorMessage())->toContain('field is required');
86+
});

0 commit comments

Comments
 (0)