Commit d77ec66203260c05495af0e172cc274812f13ab6
Committed by
GitHub
Merge pull request #4181 from vvlladd28/improvement/twilio-sms-provider
Twilio SMS provider added support Phone Number's SID or Messaging Service SID
Showing
6 changed files
with
29 additions
and
12 deletions
... | ... | @@ -24,7 +24,7 @@ import java.util.regex.Pattern; |
24 | 24 | @Slf4j |
25 | 25 | public abstract class AbstractSmsSender implements SmsSender { |
26 | 26 | |
27 | - private static final Pattern E_164_PHONE_NUMBER_PATTERN = Pattern.compile("^\\+[1-9]\\d{1,14}$"); | |
27 | + protected static final Pattern E_164_PHONE_NUMBER_PATTERN = Pattern.compile("^\\+[1-9]\\d{1,14}$"); | |
28 | 28 | |
29 | 29 | private static final int MAX_SMS_MESSAGE_LENGTH = 1600; |
30 | 30 | private static final int MAX_SMS_SEGMENT_LENGTH = 70; | ... | ... |
... | ... | @@ -19,21 +19,34 @@ import com.twilio.http.TwilioRestClient; |
19 | 19 | import com.twilio.rest.api.v2010.account.Message; |
20 | 20 | import com.twilio.type.PhoneNumber; |
21 | 21 | import org.apache.commons.lang3.StringUtils; |
22 | +import org.thingsboard.rule.engine.api.sms.exception.SmsParseException; | |
22 | 23 | import org.thingsboard.server.common.data.sms.config.TwilioSmsProviderConfiguration; |
23 | 24 | import org.thingsboard.rule.engine.api.sms.exception.SmsException; |
24 | 25 | import org.thingsboard.rule.engine.api.sms.exception.SmsSendException; |
25 | 26 | import org.thingsboard.server.service.sms.AbstractSmsSender; |
26 | 27 | |
28 | +import java.util.regex.Pattern; | |
29 | + | |
27 | 30 | public class TwilioSmsSender extends AbstractSmsSender { |
28 | 31 | |
32 | + private static final Pattern PHONE_NUMBERS_SID_MESSAGE_SERVICE_SID = Pattern.compile("^(PN|MG).*$"); | |
33 | + | |
29 | 34 | private TwilioRestClient twilioRestClient; |
30 | 35 | private String numberFrom; |
31 | 36 | |
37 | + private String validatePhoneTwilioNumber(String phoneNumber) throws SmsParseException { | |
38 | + phoneNumber = phoneNumber.trim(); | |
39 | + if (!E_164_PHONE_NUMBER_PATTERN.matcher(phoneNumber).matches() && !PHONE_NUMBERS_SID_MESSAGE_SERVICE_SID.matcher(phoneNumber).matches()) { | |
40 | + throw new SmsParseException("Invalid phone number format. Phone number must be in E.164 format/Phone Number's SID/Messaging Service SID."); | |
41 | + } | |
42 | + return phoneNumber; | |
43 | + } | |
44 | + | |
32 | 45 | public TwilioSmsSender(TwilioSmsProviderConfiguration config) { |
33 | 46 | if (StringUtils.isEmpty(config.getAccountSid()) || StringUtils.isEmpty(config.getAccountToken()) || StringUtils.isEmpty(config.getNumberFrom())) { |
34 | 47 | throw new IllegalArgumentException("Invalid twilio sms provider configuration: accountSid, accountToken and numberFrom should be specified!"); |
35 | 48 | } |
36 | - this.numberFrom = this.validatePhoneNumber(config.getNumberFrom()); | |
49 | + this.numberFrom = this.validatePhoneTwilioNumber(config.getNumberFrom()); | |
37 | 50 | this.twilioRestClient = new TwilioRestClient.Builder(config.getAccountSid(), config.getAccountToken()).build(); |
38 | 51 | } |
39 | 52 | ... | ... |
... | ... | @@ -18,14 +18,14 @@ |
18 | 18 | <form [formGroup]="twilioSmsProviderConfigurationFormGroup" style="padding-bottom: 16px;"> |
19 | 19 | <mat-form-field class="mat-block"> |
20 | 20 | <mat-label translate>admin.number-from</mat-label> |
21 | - <input type="tel" required [pattern]="phoneNumberPattern" matInput formControlName="numberFrom"> | |
21 | + <input type="tel" required [pattern]="phoneNumberPatternTwilio" matInput formControlName="numberFrom"> | |
22 | 22 | <mat-error *ngIf="twilioSmsProviderConfigurationFormGroup.get('numberFrom').hasError('required')"> |
23 | 23 | {{ 'admin.number-from-required' | translate }} |
24 | 24 | </mat-error> |
25 | 25 | <mat-error *ngIf="twilioSmsProviderConfigurationFormGroup.get('numberFrom').hasError('pattern')"> |
26 | - {{ 'admin.phone-number-pattern' | translate }} | |
26 | + {{ 'admin.phone-number-pattern-twilio' | translate }} | |
27 | 27 | </mat-error> |
28 | - <mat-hint innerHTML="{{ 'admin.phone-number-hint' | translate }}"></mat-hint> | |
28 | + <mat-hint innerHTML="{{ 'admin.phone-number-hint-twilio' | translate }}"></mat-hint> | |
29 | 29 | </mat-form-field> |
30 | 30 | <mat-form-field class="mat-block"> |
31 | 31 | <mat-label translate>admin.twilio-account-sid</mat-label> |
... | ... | @@ -36,7 +36,7 @@ |
36 | 36 | </mat-form-field> |
37 | 37 | <mat-form-field class="mat-block"> |
38 | 38 | <mat-label translate>admin.twilio-account-token</mat-label> |
39 | - <input required type="password" matInput formControlName="accountToken"> | |
39 | + <input required type="password" autocomplete="new-password" matInput formControlName="accountToken"> | |
40 | 40 | <mat-error *ngIf="twilioSmsProviderConfigurationFormGroup.get('accountToken').hasError('required')"> |
41 | 41 | {{ 'admin.twilio-account-token-required' | translate }} |
42 | 42 | </mat-error> | ... | ... |
... | ... | @@ -21,8 +21,9 @@ import { AppState } from '@app/core/core.state'; |
21 | 21 | import { coerceBooleanProperty } from '@angular/cdk/coercion'; |
22 | 22 | import { isDefinedAndNotNull } from '@core/utils'; |
23 | 23 | import { |
24 | - phoneNumberPattern, | |
25 | - SmsProviderConfiguration, SmsProviderType, | |
24 | + phoneNumberPatternTwilio, | |
25 | + SmsProviderConfiguration, | |
26 | + SmsProviderType, | |
26 | 27 | TwilioSmsProviderConfiguration |
27 | 28 | } from '@shared/models/settings.models'; |
28 | 29 | |
... | ... | @@ -40,7 +41,7 @@ export class TwilioSmsProviderConfigurationComponent implements ControlValueAcce |
40 | 41 | |
41 | 42 | twilioSmsProviderConfigurationFormGroup: FormGroup; |
42 | 43 | |
43 | - phoneNumberPattern = phoneNumberPattern; | |
44 | + phoneNumberPatternTwilio = phoneNumberPatternTwilio; | |
44 | 45 | |
45 | 46 | private requiredValue: boolean; |
46 | 47 | |
... | ... | @@ -71,9 +72,9 @@ export class TwilioSmsProviderConfigurationComponent implements ControlValueAcce |
71 | 72 | |
72 | 73 | ngOnInit() { |
73 | 74 | this.twilioSmsProviderConfigurationFormGroup = this.fb.group({ |
74 | - numberFrom: [null, [Validators.required, Validators.pattern(phoneNumberPattern)]], | |
75 | - accountSid: [null, [Validators.required]], | |
76 | - accountToken: [null, [Validators.required]] | |
75 | + numberFrom: [null, [Validators.required, Validators.pattern(phoneNumberPatternTwilio)]], | |
76 | + accountSid: [null, Validators.required], | |
77 | + accountToken: [null, Validators.required] | |
77 | 78 | }); |
78 | 79 | this.twilioSmsProviderConfigurationFormGroup.valueChanges.subscribe(() => { |
79 | 80 | this.updateModel(); | ... | ... |
... | ... | @@ -122,7 +122,9 @@ |
122 | 122 | "number-to": "Phone Number To", |
123 | 123 | "number-to-required": "Phone Number To is required.", |
124 | 124 | "phone-number-hint": "Phone Number in E.164 format, ex. +19995550123", |
125 | + "phone-number-hint-twilio": "Phone Number in E.164 format/Phone Number's SID/Messaging Service SID, ex. +19995550123/PNXXX/MGXXX", | |
125 | 126 | "phone-number-pattern": "Invalid phone number. Should be in E.164 format, ex. +19995550123.", |
127 | + "phone-number-pattern-twilio": "Invalid phone number. Should be in E.164 format/Phone Number's SID/Messaging Service SID, ex. +19995550123/PNXXX/MGXXX.", | |
126 | 128 | "sms-message": "SMS message", |
127 | 129 | "sms-message-required": "SMS message is required.", |
128 | 130 | "sms-message-max-length": "SMS message can't be longer 1600 characters", | ... | ... |