From f7911cc1acf0a4e6563835b2b716fd7cbb25634a Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Wed, 1 Dec 2021 13:47:05 +0200 Subject: [PATCH 1/4] SMPP SMS sender --- application/pom.xml | 4 + .../service/sms/DefaultSmsSenderFactory.java | 4 + .../service/sms/smpp/SmppSmsSender.java | 154 ++++++++++++++++++ .../config/SmppSmsProviderConfiguration.java | 63 +++++++ .../sms/config/SmsProviderConfiguration.java | 4 +- .../data/sms/config/SmsProviderType.java | 3 +- pom.xml | 6 + 7 files changed, 236 insertions(+), 2 deletions(-) create mode 100644 application/src/main/java/org/thingsboard/server/service/sms/smpp/SmppSmsSender.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/sms/config/SmppSmsProviderConfiguration.java diff --git a/application/pom.xml b/application/pom.xml index cc3bcef183..81fd6593c9 100644 --- a/application/pom.xml +++ b/application/pom.xml @@ -249,6 +249,10 @@ io.grpc grpc-stub + + org.opensmpp + opensmpp-core + org.thingsboard springfox-boot-starter diff --git a/application/src/main/java/org/thingsboard/server/service/sms/DefaultSmsSenderFactory.java b/application/src/main/java/org/thingsboard/server/service/sms/DefaultSmsSenderFactory.java index c4b56dbb03..d86899219e 100644 --- a/application/src/main/java/org/thingsboard/server/service/sms/DefaultSmsSenderFactory.java +++ b/application/src/main/java/org/thingsboard/server/service/sms/DefaultSmsSenderFactory.java @@ -19,9 +19,11 @@ import org.springframework.stereotype.Component; import org.thingsboard.rule.engine.api.sms.SmsSender; import org.thingsboard.rule.engine.api.sms.SmsSenderFactory; import org.thingsboard.server.common.data.sms.config.AwsSnsSmsProviderConfiguration; +import org.thingsboard.server.common.data.sms.config.SmppSmsProviderConfiguration; import org.thingsboard.server.common.data.sms.config.SmsProviderConfiguration; import org.thingsboard.server.common.data.sms.config.TwilioSmsProviderConfiguration; import org.thingsboard.server.service.sms.aws.AwsSmsSender; +import org.thingsboard.server.service.sms.smpp.SmppSmsSender; import org.thingsboard.server.service.sms.twilio.TwilioSmsSender; @Component @@ -34,6 +36,8 @@ public class DefaultSmsSenderFactory implements SmsSenderFactory { return new AwsSmsSender((AwsSnsSmsProviderConfiguration)config); case TWILIO: return new TwilioSmsSender((TwilioSmsProviderConfiguration)config); + case SMPP: + return new SmppSmsSender((SmppSmsProviderConfiguration) config); default: throw new RuntimeException("Unknown SMS provider type " + config.getType()); } diff --git a/application/src/main/java/org/thingsboard/server/service/sms/smpp/SmppSmsSender.java b/application/src/main/java/org/thingsboard/server/service/sms/smpp/SmppSmsSender.java new file mode 100644 index 0000000000..eb39b1d8ca --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/sms/smpp/SmppSmsSender.java @@ -0,0 +1,154 @@ +package org.thingsboard.server.service.sms.smpp; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.exception.ExceptionUtils; +import org.smpp.Connection; +import org.smpp.Data; +import org.smpp.Session; +import org.smpp.TCPIPConnection; +import org.smpp.TimeoutException; +import org.smpp.WrongSessionStateException; +import org.smpp.pdu.Address; +import org.smpp.pdu.BindReceiver; +import org.smpp.pdu.BindRequest; +import org.smpp.pdu.BindResponse; +import org.smpp.pdu.BindTransciever; +import org.smpp.pdu.BindTransmitter; +import org.smpp.pdu.PDUException; +import org.smpp.pdu.SubmitSM; +import org.smpp.pdu.SubmitSMResp; +import org.thingsboard.rule.engine.api.sms.exception.SmsException; +import org.thingsboard.server.common.data.sms.config.SmppSmsProviderConfiguration; +import org.thingsboard.server.service.sms.AbstractSmsSender; + +import java.io.IOException; +import java.util.Optional; + +@Slf4j +public class SmppSmsSender extends AbstractSmsSender { + private final SmppSmsProviderConfiguration config; + + private Session smppSession; + + public SmppSmsSender(SmppSmsProviderConfiguration config) { + this.config = config; + } + + @Override + public int sendSms(String numberTo, String message) throws SmsException { + try { + checkSmppSession(); + + SubmitSM request = new SubmitSM(); + if (StringUtils.isNotEmpty(config.getServiceType())) { + request.setServiceType(config.getServiceType()); + } + if (StringUtils.isNotEmpty(config.getSourceAddress())) { + request.setSourceAddr(new Address(config.getTon(), config.getNpi(), config.getSourceAddress())); + } + numberTo = prepareNumber(numberTo); + request.setDestAddr(new Address(config.getDestinationTon(), config.getDestinationNpi(), numberTo)); + request.setShortMessage(message); + request.setDataCoding(Optional.ofNullable(config.getCodingScheme()).orElse((byte) 0)); + request.setReplaceIfPresentFlag((byte) 0); + request.setEsmClass((byte) 0); + request.setProtocolId((byte) 0); + request.setPriorityFlag((byte) 0); + request.setRegisteredDelivery((byte) 0); + request.setSmDefaultMsgId((byte) 0); + + SubmitSMResp response = smppSession.submit(request); + + log.info("SMPP submit command status: {}", response.getCommandStatus()); + } catch (Exception e) { + throw new RuntimeException(e); + } + + return countMessageSegments(message); + } + + public synchronized void checkSmppSession() { + if (smppSession == null || !smppSession.isOpened()) { + smppSession = initSmppSession(); + } + } + + private Session initSmppSession() { + try { + Connection connection = new TCPIPConnection(config.getHost(), config.getPort()); + Session session = new Session(connection); + + BindRequest bindRequest; + if (config.getBindType() == null) { + bindRequest = new BindTransmitter(); + } else { + switch (config.getBindType()) { + case TX: + bindRequest = new BindTransmitter(); + break; + case RX: + bindRequest = new BindReceiver(); + break; + case TRX: + bindRequest = new BindTransciever(); + break; + default: + throw new UnsupportedOperationException("Unsupported bind type " + config.getBindType()); + } + } + + bindRequest.setSystemId(config.getSystemId()); + bindRequest.setPassword(config.getPassword()); + + byte interfaceVersion; + switch (config.getProtocolVersion()) { + case "3.3": + interfaceVersion = Data.SMPP_V33; + break; + case "3.4": + interfaceVersion = Data.SMPP_V34; + break; + default: + throw new UnsupportedOperationException("Unsupported SMPP version: " + config.getProtocolVersion()); + } + bindRequest.setInterfaceVersion(interfaceVersion); + + if (StringUtils.isNotEmpty(config.getSystemType())) { + bindRequest.setSystemType(config.getSystemType()); + } + if (StringUtils.isNotEmpty(config.getAddressRange())) { + bindRequest.setAddressRange(config.getDestinationTon(), config.getDestinationNpi(), config.getAddressRange()); + } + + BindResponse bindResponse = session.bind(bindRequest); + log.debug("SMPP bind response: {}", bindResponse.debugString()); + + if (bindResponse.getCommandStatus() != 0) { + throw new IllegalStateException("Error status when binding: " + bindResponse.getCommandStatus()); + } + + return session; + } catch (Exception e) { + throw new IllegalArgumentException("Failed to establish SMPP session: " + ExceptionUtils.getRootCauseMessage(e)); + } + } + + private String prepareNumber(String number) { + if (config.getDestinationTon() == Data.GSM_TON_INTERNATIONAL) { + return StringUtils.removeStart(number, "+"); + } + return number; + } + + @Override + public void destroy() { + try { + smppSession.unbind(); + smppSession.close(); + } catch (TimeoutException | PDUException | IOException | WrongSessionStateException e) { + throw new RuntimeException(e); + } + + } +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/sms/config/SmppSmsProviderConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/sms/config/SmppSmsProviderConfiguration.java new file mode 100644 index 0000000000..f5fccc8e0e --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/sms/config/SmppSmsProviderConfiguration.java @@ -0,0 +1,63 @@ +package org.thingsboard.server.common.data.sms.config; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +@Data +public class SmppSmsProviderConfiguration implements SmsProviderConfiguration { + @ApiModelProperty(allowableValues = "3.3, 3.4") + private String protocolVersion; + + private String host; + private Integer port; + + private String systemId; + private String password; + + @ApiModelProperty(required = false) + private String systemType; + @ApiModelProperty(value = "TX - Transmitter, RX - Receiver, TRX - Transciever. By default TX is used", required = false) + private SmppBindType bindType; + @ApiModelProperty(required = false) + private String serviceType; + + @ApiModelProperty(required = false) + private Byte ton; + @ApiModelProperty(required = false) + private Byte npi; + @ApiModelProperty(required = false) + private String sourceAddress; + + @ApiModelProperty(required = false) + private Byte destinationTon; + @ApiModelProperty(required = false) + private Byte destinationNpi; + @ApiModelProperty(required = false) + private String addressRange; + + @ApiModelProperty(allowableValues = "0-10,13-14", + value = "0 - SMSC Default Alphabet (ASCII for short and long code and to GSM for toll-free, used as default)\n" + + "1 - IA5 (ASCII for short and long code, Latin 9 for toll-free (ISO-8859-9))\n" + + "2 - Octet Unspecified (8-bit binary)\n" + + "3 - Latin 1 (ISO-8859-1)\n" + + "4 - Octet Unspecified (8-bit binary)\n" + + "5 - JIS (X 0208-1990)\n" + + "6 - Cyrillic (ISO-8859-5)\n" + + "7 - Latin/Hebrew (ISO-8859-8)\n" + + "8 - UCS2/UTF-16 (ISO/IEC-10646)\n" + + "9 - Pictogram Encoding\n" + + "10 - Music Codes (ISO-2022-JP)\n" + + "13 - Extended Kanji JIS (X 0212-1990)\n" + + "14 - Korean Graphic Character Set (KS C 5601/KS X 1001)", required = false) + private Byte codingScheme; + + @Override + public SmsProviderType getType() { + return SmsProviderType.SMPP; + } + + public enum SmppBindType { + TX, RX, TRX + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/sms/config/SmsProviderConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/sms/config/SmsProviderConfiguration.java index 34f187b005..08968534b7 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/sms/config/SmsProviderConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/sms/config/SmsProviderConfiguration.java @@ -27,7 +27,9 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo; property = "type") @JsonSubTypes({ @JsonSubTypes.Type(value = AwsSnsSmsProviderConfiguration.class, name = "AWS_SNS"), - @JsonSubTypes.Type(value = TwilioSmsProviderConfiguration.class, name = "TWILIO")}) + @JsonSubTypes.Type(value = TwilioSmsProviderConfiguration.class, name = "TWILIO"), + @JsonSubTypes.Type(value = SmppSmsProviderConfiguration.class, name = "SMPP") +}) public interface SmsProviderConfiguration { @JsonIgnore diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/sms/config/SmsProviderType.java b/common/data/src/main/java/org/thingsboard/server/common/data/sms/config/SmsProviderType.java index 33ccf440ab..f6390433cb 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/sms/config/SmsProviderType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/sms/config/SmsProviderType.java @@ -17,5 +17,6 @@ package org.thingsboard.server.common.data.sms.config; public enum SmsProviderType { AWS_SNS, - TWILIO + TWILIO, + SMPP } diff --git a/pom.xml b/pom.xml index 7503bcc4c0..86a33271af 100755 --- a/pom.xml +++ b/pom.xml @@ -130,6 +130,7 @@ 1.16.0 1.12 + 3.0.0 @@ -1872,6 +1873,11 @@ ${zeroturnaround.version} test + + org.opensmpp + opensmpp-core + ${opensmpp.version} + From 7b0309ca683b6893014de41a9926d30ca9c3e22d Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Thu, 2 Dec 2021 16:06:29 +0200 Subject: [PATCH 2/4] SMPP SMS sender refactoring, API docs --- .../service/sms/smpp/SmppSmsSender.java | 69 ++++++++++++----- .../config/SmppSmsProviderConfiguration.java | 76 ++++++++++++++++--- 2 files changed, 114 insertions(+), 31 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/sms/smpp/SmppSmsSender.java b/application/src/main/java/org/thingsboard/server/service/sms/smpp/SmppSmsSender.java index eb39b1d8ca..e89a346773 100644 --- a/application/src/main/java/org/thingsboard/server/service/sms/smpp/SmppSmsSender.java +++ b/application/src/main/java/org/thingsboard/server/service/sms/smpp/SmppSmsSender.java @@ -1,3 +1,18 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package org.thingsboard.server.service.sms.smpp; import lombok.extern.slf4j.Slf4j; @@ -32,7 +47,26 @@ public class SmppSmsSender extends AbstractSmsSender { private Session smppSession; public SmppSmsSender(SmppSmsProviderConfiguration config) { + if (config.getBindType() == null) { + config.setBindType(SmppSmsProviderConfiguration.SmppBindType.TX); + } + if (StringUtils.isNotEmpty(config.getSourceAddress())) { + if (config.getSourceTon() == null) { + config.setSourceTon((byte) 5); + } + if (config.getSourceNpi() == null) { + config.setSourceNpi((byte) 0); + } + } + if (config.getDestinationTon() == null) { + config.setDestinationTon((byte) 5); + } + if (config.getDestinationNpi() == null) { + config.setDestinationNpi((byte) 0); + } + this.config = config; + initSmppSession(); } @Override @@ -45,10 +79,9 @@ public class SmppSmsSender extends AbstractSmsSender { request.setServiceType(config.getServiceType()); } if (StringUtils.isNotEmpty(config.getSourceAddress())) { - request.setSourceAddr(new Address(config.getTon(), config.getNpi(), config.getSourceAddress())); + request.setSourceAddr(new Address(config.getSourceTon(), config.getSourceNpi(), config.getSourceAddress())); } - numberTo = prepareNumber(numberTo); - request.setDestAddr(new Address(config.getDestinationTon(), config.getDestinationNpi(), numberTo)); + request.setDestAddr(new Address(config.getDestinationTon(), config.getDestinationNpi(), prepareNumber(numberTo))); request.setShortMessage(message); request.setDataCoding(Optional.ofNullable(config.getCodingScheme()).orElse((byte) 0)); request.setReplaceIfPresentFlag((byte) 0); @@ -69,7 +102,7 @@ public class SmppSmsSender extends AbstractSmsSender { } public synchronized void checkSmppSession() { - if (smppSession == null || !smppSession.isOpened()) { + if (!smppSession.isOpened()) { smppSession = initSmppSession(); } } @@ -80,22 +113,18 @@ public class SmppSmsSender extends AbstractSmsSender { Session session = new Session(connection); BindRequest bindRequest; - if (config.getBindType() == null) { - bindRequest = new BindTransmitter(); - } else { - switch (config.getBindType()) { - case TX: - bindRequest = new BindTransmitter(); - break; - case RX: - bindRequest = new BindReceiver(); - break; - case TRX: - bindRequest = new BindTransciever(); - break; - default: - throw new UnsupportedOperationException("Unsupported bind type " + config.getBindType()); - } + switch (config.getBindType()) { + case TX: + bindRequest = new BindTransmitter(); + break; + case RX: + bindRequest = new BindReceiver(); + break; + case TRX: + bindRequest = new BindTransciever(); + break; + default: + throw new UnsupportedOperationException("Unsupported bind type " + config.getBindType()); } bindRequest.setSystemId(config.getSystemId()); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/sms/config/SmppSmsProviderConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/sms/config/SmppSmsProviderConfiguration.java index f5fccc8e0e..4d9b53e10c 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/sms/config/SmppSmsProviderConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/sms/config/SmppSmsProviderConfiguration.java @@ -1,3 +1,18 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package org.thingsboard.server.common.data.sms.config; import io.swagger.annotations.ApiModelProperty; @@ -5,34 +20,73 @@ import lombok.Data; @Data public class SmppSmsProviderConfiguration implements SmsProviderConfiguration { - @ApiModelProperty(allowableValues = "3.3, 3.4") + @ApiModelProperty(value = "SMPP version", allowableValues = "3.3, 3.4", required = true) private String protocolVersion; + @ApiModelProperty(value = "SMPP host", required = true) private String host; + @ApiModelProperty(value = "SMPP port", required = true) private Integer port; + @ApiModelProperty(value = "System ID", required = true) private String systemId; + @ApiModelProperty(value = "Password", required = true) private String password; - @ApiModelProperty(required = false) + @ApiModelProperty(value = "System type", required = false) private String systemType; @ApiModelProperty(value = "TX - Transmitter, RX - Receiver, TRX - Transciever. By default TX is used", required = false) private SmppBindType bindType; - @ApiModelProperty(required = false) + @ApiModelProperty(value = "Service type", required = false) private String serviceType; - @ApiModelProperty(required = false) - private Byte ton; - @ApiModelProperty(required = false) - private Byte npi; - @ApiModelProperty(required = false) + @ApiModelProperty(value = "Source address", required = false) private String sourceAddress; + @ApiModelProperty(value = "Source TON (Type of Number). Needed is source address is set. 5 by default.\n" + + "0 - Unknown\n" + + "1 - International\n" + + "2 - National\n" + + "3 - Network Specific\n" + + "4 - Subscriber Number\n" + + "5 - Alphanumeric\n" + + "6 - Abbreviated", required = false) + private Byte sourceTon; + @ApiModelProperty(value = "Source NPI (Numbering Plan Identification). Needed is source address is set. 0 by default.\n" + + "0 - Unknown\n" + + "1 - ISDN/telephone numbering plan (E163/E164)\n" + + "3 - Data numbering plan (X.121)\n" + + "4 - Telex numbering plan (F.69)\n" + + "6 - Land Mobile (E.212) =6\n" + + "8 - National numbering plan\n" + + "9 - Private numbering plan\n" + + "10 - ERMES numbering plan (ETSI DE/PS 3 01-3)\n" + + "13 - Internet (IP)\n" + + "18 - WAP Client Id (to be defined by WAP Forum)", required = false) + private Byte sourceNpi; - @ApiModelProperty(required = false) + @ApiModelProperty(value = "Destination TON (Type of Number). 5 by default.\n" + + "0 - Unknown\n" + + "1 - International\n" + + "2 - National\n" + + "3 - Network Specific\n" + + "4 - Subscriber Number\n" + + "5 - Alphanumeric\n" + + "6 - Abbreviated", required = false) private Byte destinationTon; - @ApiModelProperty(required = false) + @ApiModelProperty(value = "Destination NPI (Numbering Plan Identification). 0 by default.\n" + + "0 - Unknown\n" + + "1 - ISDN/telephone numbering plan (E163/E164)\n" + + "3 - Data numbering plan (X.121)\n" + + "4 - Telex numbering plan (F.69)\n" + + "6 - Land Mobile (E.212) =6\n" + + "8 - National numbering plan\n" + + "9 - Private numbering plan\n" + + "10 - ERMES numbering plan (ETSI DE/PS 3 01-3)\n" + + "13 - Internet (IP)\n" + + "18 - WAP Client Id (to be defined by WAP Forum)", required = false) private Byte destinationNpi; - @ApiModelProperty(required = false) + + @ApiModelProperty(value = "Address range", required = false) private String addressRange; @ApiModelProperty(allowableValues = "0-10,13-14", From f17380597f9ba93fb7c2f1fa6718d6fd80e77d28 Mon Sep 17 00:00:00 2001 From: Kalutka Zhenya Date: Fri, 28 Jan 2022 12:17:21 +0200 Subject: [PATCH 3/4] Added main UI functional --- .../home/components/home-components.module.ts | 3 + ...-sms-provider-configuration.component.html | 101 +++++++++++ ...pp-sms-provider-configuration.component.ts | 163 ++++++++++++++++++ .../sms-provider-configuration.component.html | 6 + .../src/app/shared/models/settings.models.ts | 57 +++++- .../assets/locale/locale.constant-en_US.json | 1 + 6 files changed, 326 insertions(+), 5 deletions(-) create mode 100644 ui-ngx/src/app/modules/home/components/sms/smpp-sms-provider-configuration.component.html create mode 100644 ui-ngx/src/app/modules/home/components/sms/smpp-sms-provider-configuration.component.ts diff --git a/ui-ngx/src/app/modules/home/components/home-components.module.ts b/ui-ngx/src/app/modules/home/components/home-components.module.ts index 5e0752fcbc..a4a8d6ca12 100644 --- a/ui-ngx/src/app/modules/home/components/home-components.module.ts +++ b/ui-ngx/src/app/modules/home/components/home-components.module.ts @@ -117,6 +117,7 @@ import { DefaultTenantProfileConfigurationComponent } from '@home/components/pro import { TenantProfileConfigurationComponent } from '@home/components/profile/tenant/tenant-profile-configuration.component'; import { SmsProviderConfigurationComponent } from '@home/components/sms/sms-provider-configuration.component'; import { AwsSnsProviderConfigurationComponent } from '@home/components/sms/aws-sns-provider-configuration.component'; +import { SmppSmsProviderConfigurationComponent } from '@home/components/sms/smpp-sms-provider-configuration.component'; import { TwilioSmsProviderConfigurationComponent } from '@home/components/sms/twilio-sms-provider-configuration.component'; import { Lwm2mProfileComponentsModule } from '@home/components/profile/device/lwm2m/lwm2m-profile-components.module'; import { DashboardPageComponent } from '@home/components/dashboard-page/dashboard-page.component'; @@ -245,6 +246,7 @@ import { DeviceProfileCommonModule } from '@home/components/profile/device/commo EditAlarmDetailsDialogComponent, SmsProviderConfigurationComponent, AwsSnsProviderConfigurationComponent, + SmppSmsProviderConfigurationComponent, TwilioSmsProviderConfigurationComponent, DashboardToolbarComponent, DashboardPageComponent, @@ -356,6 +358,7 @@ import { DeviceProfileCommonModule } from '@home/components/profile/device/commo AlarmScheduleComponent, SmsProviderConfigurationComponent, AwsSnsProviderConfigurationComponent, + SmppSmsProviderConfigurationComponent, TwilioSmsProviderConfigurationComponent, DashboardToolbarComponent, DashboardPageComponent, diff --git a/ui-ngx/src/app/modules/home/components/sms/smpp-sms-provider-configuration.component.html b/ui-ngx/src/app/modules/home/components/sms/smpp-sms-provider-configuration.component.html new file mode 100644 index 0000000000..e8c3de5943 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/sms/smpp-sms-provider-configuration.component.html @@ -0,0 +1,101 @@ +
+ + SMPP version + + + SMPP version is required + + + + SMPP host + + + SMPP host is required + + + + SMPP port + + + SMPP port is required + + + + System ID + + + System ID is required + + + + Password + + + Password is required + + + + System type + + + + Bind type + + + {{bindType.name}} + + + + + Service type + + + + Source address + + + + Source TON + + + {{sourceTon.name}} + + + + + Source NPI + + + {{sourceNpi.name}} + + + + + Destination TON (Type of Number) + + + {{destinationTon.name}} + + + + + Destination NPI (Numbering Plan Identification) + + + {{destinationNpi.name}} + + + + + Address range + + + + Coding Scheme + + + {{codingScheme.name}} + + + +
diff --git a/ui-ngx/src/app/modules/home/components/sms/smpp-sms-provider-configuration.component.ts b/ui-ngx/src/app/modules/home/components/sms/smpp-sms-provider-configuration.component.ts new file mode 100644 index 0000000000..2cb7d8cbfb --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/sms/smpp-sms-provider-configuration.component.ts @@ -0,0 +1,163 @@ +import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; +import { + AwsSnsSmsProviderConfiguration, SmppSmsProviderConfiguration, + SmsProviderConfiguration, + SmsProviderType +} from '@shared/models/settings.models'; +import { isDefinedAndNotNull } from '@core/utils'; +import { coerceBooleanProperty } from '@angular/cdk/coercion'; + +@Component({ + selector: 'tb-smpp-sms-provider-configuration', + templateUrl: './smpp-sms-provider-configuration.component.html', + styleUrls: [], + providers: [{ + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => SmppSmsProviderConfigurationComponent), + multi: true + }] +}) +export class SmppSmsProviderConfigurationComponent implements ControlValueAccessor, OnInit{ + constructor(private fb: FormBuilder) { + } + private requiredValue: boolean; + + get required(): boolean { + return this.requiredValue; + } + + @Input() + set required(value: boolean) { + this.requiredValue = coerceBooleanProperty(value); + } + @Input() + disabled: boolean; + + smppSmsProviderConfigurationFormGroup: FormGroup; + bindTypes = [ + {value: 'TX', name: 'Transmitter'}, + {value: 'RX', name: 'Receiver'}, + {value: 'TRX', name: 'Transciever'}, + ] + + sourcesTon = [ + {value: 0, name: 'Unknown'}, + {value: 1, name: 'International'}, + {value: 2, name: 'National'}, + {value: 3, name: 'Network Specific'}, + {value: 4, name: 'Subscriber Number'}, + {value: 5, name: 'Alphanumeric'}, + {value: 6, name: 'Abbreviated'} + ] + + sourcesNpi = [ + {value: 0, name: 'Unknown'}, + {value: 1, name: 'ISDN/telephone numbering plan (E163/E164)'}, + {value: 3, name: 'Data numbering plan (X.121)'}, + {value: 4, name: 'Telex numbering plan (F.69)'}, + {value: 5, name: 'Land Mobile (E.212)'}, + {value: 8, name: 'National numbering plan'}, + {value: 9, name: 'Private numbering plan'}, + {value: 10, name: 'ERMES numbering plan (ETSI DE/PS 3 01-3)'}, + {value: 13, name: 'Internet (IP)'}, + {value: 18, name: 'WAP Client Id (to be defined by WAP Forum)'}, + ] + + destinationsTon = [ + {value: 0, name: 'Unknown'}, + {value: 1, name: 'International'}, + {value: 2, name: 'National'}, + {value: 3, name: 'Network Specific'}, + {value: 4, name: 'Subscriber Number'}, + {value: 5, name: 'Alphanumeric'}, + {value: 6, name: 'Abbreviated'}, + ] + + destinationsNpi = [ + {value: 0, name: 'Unknown'}, + {value: 1, name: 'ISDN/telephone numbering plan (E163/E164)'}, + {value: 3, name: 'Data numbering plan (X.121)'}, + {value: 4, name: 'Telex numbering plan (F.69)'}, + {value: 6, name: 'Land Mobile (E.212)'}, + {value: 8, name: 'National numbering plan'}, + {value: 9, name: 'Private numbering plan'}, + {value: 10, name: 'ERMES numbering plan (ETSI DE/PS 3 01-3)'}, + {value: 13, name: 'Internet (IP)'}, + {value: 18, name: 'WAP Client Id (to be defined by WAP Forum)'}, + ] + + codingSchemes = [ + {value: 0, name: 'SMSC Default Alphabet (ASCII for short and long code and to GSM for toll-free)'}, + {value: 1, name: 'IA5 (ASCII for short and long code, Latin 9 for toll-free (ISO-8859-9))'}, + {value: 2, name: 'Octet Unspecified (8-bit binary)'}, + {value: 3, name: 'Latin 1 (ISO-8859-1)'}, + {value: 4, name: 'Octet Unspecified (8-bit binary)'}, + {value: 5, name: 'JIS (X 0208-1990)'}, + {value: 6, name: 'Cyrillic (ISO-8859-5)'}, + {value: 7, name: 'Latin/Hebrew (ISO-8859-8)'}, + {value: 8, name: 'UCS2/UTF-16 (ISO/IEC-10646)'}, + {value: 9, name: 'Pictogram Encoding'}, + {value: 10, name: 'Music Codes (ISO-2022-JP)'}, + {value: 13, name: 'Extended Kanji JIS (X 0212-1990)'}, + {value: 14, name: 'Korean Graphic Character Set (KS C 5601/KS X 1001)'}, + ] + + private propagateChange = (v: any) => { }; + + ngOnInit(): void { + this.smppSmsProviderConfigurationFormGroup = this.fb.group({ + protocolVersion: [null, [Validators.required]], + host: [null, [Validators.required]], + port: [null, [Validators.required]], + systemId: [null, [Validators.required]], + password: [null, [Validators.required]], + systemType: [null], + bindType: [null, []], + serviceType: [null, []], + sourceAddress: [null, []], + sourceTon: [null, []], + sourceNpi: [null, []], + destinationTon: [null, []], + destinationNpi: [null, []], + addressRange: [null, []], + codingScheme: [null, []], + }); + + this.smppSmsProviderConfigurationFormGroup.valueChanges.subscribe(() => { + this.updateValue(); + }); + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (this.disabled) { + this.smppSmsProviderConfigurationFormGroup.disable({emitEvent: false}); + } else { + this.smppSmsProviderConfigurationFormGroup.enable({emitEvent: false}); + } + } + + writeValue(value: AwsSnsSmsProviderConfiguration | null): void { + if (isDefinedAndNotNull(value)) { + this.smppSmsProviderConfigurationFormGroup.patchValue(value, {emitEvent: false}); + } + } + + private updateValue() { + let configuration: SmppSmsProviderConfiguration = null; + if (this.smppSmsProviderConfigurationFormGroup.valid) { + configuration = this.smppSmsProviderConfigurationFormGroup.value; + (configuration as SmsProviderConfiguration).type = SmsProviderType.SMPP; + } + this.propagateChange(configuration); + } + +} diff --git a/ui-ngx/src/app/modules/home/components/sms/sms-provider-configuration.component.html b/ui-ngx/src/app/modules/home/components/sms/sms-provider-configuration.component.html index d9964285e3..ca21d30335 100644 --- a/ui-ngx/src/app/modules/home/components/sms/sms-provider-configuration.component.html +++ b/ui-ngx/src/app/modules/home/components/sms/sms-provider-configuration.component.html @@ -40,5 +40,11 @@ formControlName="configuration"> + + + + diff --git a/ui-ngx/src/app/shared/models/settings.models.ts b/ui-ngx/src/app/shared/models/settings.models.ts index 8b2d4aac78..06e8fd83da 100644 --- a/ui-ngx/src/app/shared/models/settings.models.ts +++ b/ui-ngx/src/app/shared/models/settings.models.ts @@ -14,8 +14,9 @@ /// limitations under the License. /// -import { ValidatorFn } from '@angular/forms'; -import { isNotEmptyStr } from '@core/utils'; +import { ValidatorFn, Validators } from '@angular/forms'; +import { isNotEmptyStr, isNumber } from '@core/utils'; +import { number, string } from 'prop-types'; export const smtpPortPattern: RegExp = /^([0-9]{1,4}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$/; @@ -71,13 +72,15 @@ export const phoneNumberPatternTwilio = /^\+[1-9]\d{1,14}$|^(MG|PN).*$/; export enum SmsProviderType { AWS_SNS = 'AWS_SNS', - TWILIO = 'TWILIO' + TWILIO = 'TWILIO', + SMPP = 'SMPP' } export const smsProviderTypeTranslationMap = new Map( [ [SmsProviderType.AWS_SNS, 'admin.sms-provider-type-aws-sns'], - [SmsProviderType.TWILIO, 'admin.sms-provider-type-twilio'] + [SmsProviderType.TWILIO, 'admin.sms-provider-type-twilio'], + [SmsProviderType.SMPP, 'admin.sms-provider-type-smpp'] ] ); @@ -93,7 +96,26 @@ export interface TwilioSmsProviderConfiguration { numberFrom?: string; } -export type SmsProviderConfigurations = AwsSnsSmsProviderConfiguration & TwilioSmsProviderConfiguration; +export interface SmppSmsProviderConfiguration { + protocolVersion?: string, + host?: string, + port?: number, + systemId?: string, + password?: string, + systemType?: string, + bindType?: string, + serviceType?: string, + sourceAddress?: string, + sourceTon?: number, + sourceNpi?: number, + destinationTon?: number, + destinationNpi?: number, + addressRange?: string, + codingScheme?: number +} + + +export type SmsProviderConfigurations = SmppSmsProviderConfiguration & AwsSnsSmsProviderConfiguration & TwilioSmsProviderConfiguration; export interface SmsProviderConfiguration extends SmsProviderConfigurations { type: SmsProviderType; @@ -117,6 +139,11 @@ export function smsProviderConfigurationValidator(required: boolean): ValidatorF valid = isNotEmptyStr(twilioConfiguration.numberFrom) && isNotEmptyStr(twilioConfiguration.accountSid) && isNotEmptyStr(twilioConfiguration.accountToken); break; + case SmsProviderType.SMPP: + const smppConfiguration: SmppSmsProviderConfiguration = configuration; + valid = isNotEmptyStr(smppConfiguration.protocolVersion) && isNotEmptyStr(smppConfiguration.host) + && isNumber(smppConfiguration.port) && isNotEmptyStr(smppConfiguration.systemId) && isNotEmptyStr(smppConfiguration.password); + break; } } if (!valid) { @@ -155,6 +182,26 @@ export function createSmsProviderConfiguration(type: SmsProviderType): SmsProvid }; smsProviderConfiguration = {...twilioSmsProviderConfiguration, type: SmsProviderType.TWILIO}; break; + case SmsProviderType.SMPP: + const smppSmsProviderConfiguration: SmppSmsProviderConfiguration = { + protocolVersion: '', + host: '', + port: null, + systemId: '', + password: '', + systemType: '', + bindType: 'TX', + serviceType: '', + sourceAddress: '', + sourceTon: 5, + sourceNpi: 0, + destinationTon: 5, + destinationNpi: 0, + addressRange: '', + codingScheme: 0 + }; + smsProviderConfiguration = {...smppSmsProviderConfiguration, type: SmsProviderType.SMPP}; + break; } } return smsProviderConfiguration; diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index bd14f73e27..35bd9d759a 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -112,6 +112,7 @@ "sms-provider-type-required": "SMS provider type is required.", "sms-provider-type-aws-sns": "Amazon SNS", "sms-provider-type-twilio": "Twilio", + "sms-provider-type-smpp": "SMPP", "aws-access-key-id": "AWS Access Key ID", "aws-access-key-id-required": "AWS Access Key ID is required", "aws-secret-access-key": "AWS Secret Access Key", From d05887baa41216863a802a0adf55e3c2c4aeec57 Mon Sep 17 00:00:00 2001 From: Kalutka Zhenya Date: Fri, 28 Jan 2022 12:26:13 +0200 Subject: [PATCH 4/4] Refactoring --- ui-ngx/src/app/shared/models/settings.models.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ui-ngx/src/app/shared/models/settings.models.ts b/ui-ngx/src/app/shared/models/settings.models.ts index 06e8fd83da..fc9a7c6723 100644 --- a/ui-ngx/src/app/shared/models/settings.models.ts +++ b/ui-ngx/src/app/shared/models/settings.models.ts @@ -14,9 +14,8 @@ /// limitations under the License. /// -import { ValidatorFn, Validators } from '@angular/forms'; +import { ValidatorFn } from '@angular/forms'; import { isNotEmptyStr, isNumber } from '@core/utils'; -import { number, string } from 'prop-types'; export const smtpPortPattern: RegExp = /^([0-9]{1,4}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$/;