Commit d77ec66203260c05495af0e172cc274812f13ab6

Authored by Igor Kulikov
Committed by GitHub
2 parents 16c55b4d 07ed2581

Merge pull request #4181 from vvlladd28/improvement/twilio-sms-provider

Twilio SMS provider added support Phone Number's SID or Messaging Service SID
@@ -24,7 +24,7 @@ import java.util.regex.Pattern; @@ -24,7 +24,7 @@ import java.util.regex.Pattern;
24 @Slf4j 24 @Slf4j
25 public abstract class AbstractSmsSender implements SmsSender { 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 private static final int MAX_SMS_MESSAGE_LENGTH = 1600; 29 private static final int MAX_SMS_MESSAGE_LENGTH = 1600;
30 private static final int MAX_SMS_SEGMENT_LENGTH = 70; 30 private static final int MAX_SMS_SEGMENT_LENGTH = 70;
@@ -19,21 +19,34 @@ import com.twilio.http.TwilioRestClient; @@ -19,21 +19,34 @@ import com.twilio.http.TwilioRestClient;
19 import com.twilio.rest.api.v2010.account.Message; 19 import com.twilio.rest.api.v2010.account.Message;
20 import com.twilio.type.PhoneNumber; 20 import com.twilio.type.PhoneNumber;
21 import org.apache.commons.lang3.StringUtils; 21 import org.apache.commons.lang3.StringUtils;
  22 +import org.thingsboard.rule.engine.api.sms.exception.SmsParseException;
22 import org.thingsboard.server.common.data.sms.config.TwilioSmsProviderConfiguration; 23 import org.thingsboard.server.common.data.sms.config.TwilioSmsProviderConfiguration;
23 import org.thingsboard.rule.engine.api.sms.exception.SmsException; 24 import org.thingsboard.rule.engine.api.sms.exception.SmsException;
24 import org.thingsboard.rule.engine.api.sms.exception.SmsSendException; 25 import org.thingsboard.rule.engine.api.sms.exception.SmsSendException;
25 import org.thingsboard.server.service.sms.AbstractSmsSender; 26 import org.thingsboard.server.service.sms.AbstractSmsSender;
26 27
  28 +import java.util.regex.Pattern;
  29 +
27 public class TwilioSmsSender extends AbstractSmsSender { 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 private TwilioRestClient twilioRestClient; 34 private TwilioRestClient twilioRestClient;
30 private String numberFrom; 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 public TwilioSmsSender(TwilioSmsProviderConfiguration config) { 45 public TwilioSmsSender(TwilioSmsProviderConfiguration config) {
33 if (StringUtils.isEmpty(config.getAccountSid()) || StringUtils.isEmpty(config.getAccountToken()) || StringUtils.isEmpty(config.getNumberFrom())) { 46 if (StringUtils.isEmpty(config.getAccountSid()) || StringUtils.isEmpty(config.getAccountToken()) || StringUtils.isEmpty(config.getNumberFrom())) {
34 throw new IllegalArgumentException("Invalid twilio sms provider configuration: accountSid, accountToken and numberFrom should be specified!"); 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 this.twilioRestClient = new TwilioRestClient.Builder(config.getAccountSid(), config.getAccountToken()).build(); 50 this.twilioRestClient = new TwilioRestClient.Builder(config.getAccountSid(), config.getAccountToken()).build();
38 } 51 }
39 52
@@ -18,14 +18,14 @@ @@ -18,14 +18,14 @@
18 <form [formGroup]="twilioSmsProviderConfigurationFormGroup" style="padding-bottom: 16px;"> 18 <form [formGroup]="twilioSmsProviderConfigurationFormGroup" style="padding-bottom: 16px;">
19 <mat-form-field class="mat-block"> 19 <mat-form-field class="mat-block">
20 <mat-label translate>admin.number-from</mat-label> 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 <mat-error *ngIf="twilioSmsProviderConfigurationFormGroup.get('numberFrom').hasError('required')"> 22 <mat-error *ngIf="twilioSmsProviderConfigurationFormGroup.get('numberFrom').hasError('required')">
23 {{ 'admin.number-from-required' | translate }} 23 {{ 'admin.number-from-required' | translate }}
24 </mat-error> 24 </mat-error>
25 <mat-error *ngIf="twilioSmsProviderConfigurationFormGroup.get('numberFrom').hasError('pattern')"> 25 <mat-error *ngIf="twilioSmsProviderConfigurationFormGroup.get('numberFrom').hasError('pattern')">
26 - {{ 'admin.phone-number-pattern' | translate }} 26 + {{ 'admin.phone-number-pattern-twilio' | translate }}
27 </mat-error> 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 </mat-form-field> 29 </mat-form-field>
30 <mat-form-field class="mat-block"> 30 <mat-form-field class="mat-block">
31 <mat-label translate>admin.twilio-account-sid</mat-label> 31 <mat-label translate>admin.twilio-account-sid</mat-label>
@@ -36,7 +36,7 @@ @@ -36,7 +36,7 @@
36 </mat-form-field> 36 </mat-form-field>
37 <mat-form-field class="mat-block"> 37 <mat-form-field class="mat-block">
38 <mat-label translate>admin.twilio-account-token</mat-label> 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 <mat-error *ngIf="twilioSmsProviderConfigurationFormGroup.get('accountToken').hasError('required')"> 40 <mat-error *ngIf="twilioSmsProviderConfigurationFormGroup.get('accountToken').hasError('required')">
41 {{ 'admin.twilio-account-token-required' | translate }} 41 {{ 'admin.twilio-account-token-required' | translate }}
42 </mat-error> 42 </mat-error>
@@ -21,8 +21,9 @@ import { AppState } from '@app/core/core.state'; @@ -21,8 +21,9 @@ import { AppState } from '@app/core/core.state';
21 import { coerceBooleanProperty } from '@angular/cdk/coercion'; 21 import { coerceBooleanProperty } from '@angular/cdk/coercion';
22 import { isDefinedAndNotNull } from '@core/utils'; 22 import { isDefinedAndNotNull } from '@core/utils';
23 import { 23 import {
24 - phoneNumberPattern,  
25 - SmsProviderConfiguration, SmsProviderType, 24 + phoneNumberPatternTwilio,
  25 + SmsProviderConfiguration,
  26 + SmsProviderType,
26 TwilioSmsProviderConfiguration 27 TwilioSmsProviderConfiguration
27 } from '@shared/models/settings.models'; 28 } from '@shared/models/settings.models';
28 29
@@ -40,7 +41,7 @@ export class TwilioSmsProviderConfigurationComponent implements ControlValueAcce @@ -40,7 +41,7 @@ export class TwilioSmsProviderConfigurationComponent implements ControlValueAcce
40 41
41 twilioSmsProviderConfigurationFormGroup: FormGroup; 42 twilioSmsProviderConfigurationFormGroup: FormGroup;
42 43
43 - phoneNumberPattern = phoneNumberPattern; 44 + phoneNumberPatternTwilio = phoneNumberPatternTwilio;
44 45
45 private requiredValue: boolean; 46 private requiredValue: boolean;
46 47
@@ -71,9 +72,9 @@ export class TwilioSmsProviderConfigurationComponent implements ControlValueAcce @@ -71,9 +72,9 @@ export class TwilioSmsProviderConfigurationComponent implements ControlValueAcce
71 72
72 ngOnInit() { 73 ngOnInit() {
73 this.twilioSmsProviderConfigurationFormGroup = this.fb.group({ 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 this.twilioSmsProviderConfigurationFormGroup.valueChanges.subscribe(() => { 79 this.twilioSmsProviderConfigurationFormGroup.valueChanges.subscribe(() => {
79 this.updateModel(); 80 this.updateModel();
@@ -65,6 +65,7 @@ export interface UpdateMessage { @@ -65,6 +65,7 @@ export interface UpdateMessage {
65 } 65 }
66 66
67 export const phoneNumberPattern = /^\+[1-9]\d{1,14}$/; 67 export const phoneNumberPattern = /^\+[1-9]\d{1,14}$/;
  68 +export const phoneNumberPatternTwilio = /^\+[1-9]\d{1,14}$|^(MG|PN).*$/;
68 69
69 export enum SmsProviderType { 70 export enum SmsProviderType {
70 AWS_SNS = 'AWS_SNS', 71 AWS_SNS = 'AWS_SNS',
@@ -122,7 +122,9 @@ @@ -122,7 +122,9 @@
122 "number-to": "Phone Number To", 122 "number-to": "Phone Number To",
123 "number-to-required": "Phone Number To is required.", 123 "number-to-required": "Phone Number To is required.",
124 "phone-number-hint": "Phone Number in E.164 format, ex. +19995550123", 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 "phone-number-pattern": "Invalid phone number. Should be in E.164 format, ex. +19995550123.", 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 "sms-message": "SMS message", 128 "sms-message": "SMS message",
127 "sms-message-required": "SMS message is required.", 129 "sms-message-required": "SMS message is required.",
128 "sms-message-max-length": "SMS message can't be longer 1600 characters", 130 "sms-message-max-length": "SMS message can't be longer 1600 characters",