Example #1
0
        public async Task <ServiceProviderResponse> CreateOrUpdateTemplateAsync(
            [FromHeader(Constant.OperationTrackingIdHeader)] string requestId,
            [FromHeader] string account,
            [FromBody] TemplateCreateOrUpdateRequest request)
        {
            Validator.ArgumentNotNull(request, nameof(request));
            Validator.ArgumentNotNullOrEmpty(request.Name, nameof(request.Name));
            Validator.ArgumentNotNullOrEmpty(request.SenderAlias, nameof(request.SenderAlias));
            Validator.ArgumentNotNullOrEmpty(request.HtmlMsg, nameof(request.HtmlMsg));
            if (request.SenderAddrID != null)
            {
                Validator.ArgumentValidGuid(request.SenderAddrID, nameof(request.SenderAddrID));
            }

            var currentAccount = await EnsureAccount(account, requestId);

            var template = new Template
            {
                Name = request.Name,
                EngagementAccount = currentAccount.EngagementAccount,
                SenderId          = new Guid(request.SenderAddrID),
                SenderAlias       = request.SenderAlias,
                Subject           = request.Subject,
                HtmlMsg           = request.HtmlMsg,
                EnableUnSubscribe = request.EnableUnSubscribe,
                State             = ResourceState.Active,
            };

            var templateResult = await this.engine.CreateOrUpdateTemplateAsync(template, requestId);

            return(ServiceProviderResponse.CreateJsonResponse(HttpStatusCode.OK, templateResult));
        }
        public async Task <ServiceProviderResponse> GetMessageDetailAsync(
            [FromHeader(Constant.OperationTrackingIdHeader)] string requestId,
            [FromHeader] string account,
            string messageId,
            [FromQuery] string continuationToken = null,
            [FromQuery] int count = ContinuationToken.DefaultCount)
        {
            var currentAccount = await this.ValidateAccount(account);

            Validator.IsTrue <ArgumentException>(count > 0 && count <= SmsConstant.PagingMaxTakeCount, nameof(count), $"Count should be between 0 and {SmsConstant.PagingMaxTakeCount}.");

            var token = new AzureStorageContinuationToken(continuationToken);

            Validator.IsTrue <ArgumentException>(token.IsValid, nameof(token), "ContinuationToken is invalid.");

            var details = await this.reportManager.GetMessageAsync(account, messageId, count, token.TableContinuationToken);

            Validator.IsTrue <ArgumentException>(details != null, nameof(details), "Message does not exist.");

            var response = ServiceProviderResponse.CreateJsonResponse(HttpStatusCode.OK, details);

            if (details.ContinuationToken != null)
            {
                response.Headers = new Dictionary <string, IEnumerable <string> >
                {
                    { ContinuationToken.ContinuationTokenKey, new List <string> {
                          new AzureStorageContinuationToken(details.ContinuationToken).Token
                      } }
                };
            }

            return(response);
        }
Example #3
0
        public async Task <ServiceProviderResponse> ListTemplatesAsync(
            [FromHeader(Constant.OperationTrackingIdHeader)] string requestId,
            [FromHeader] string account,
            [FromQuery] string continuationToken = null,
            [FromQuery] int count = ContinuationToken.DefaultCount)
        {
            await ValidateAccount(account);

            Validator.IsTrue <ArgumentException>(count > 0 && count <= SmsConstant.PagingMaxTakeCount, nameof(count), $"Count should be between 0 and {SmsConstant.PagingMaxTakeCount}.");

            var token = new DbContinuationToken(continuationToken);

            Validator.IsTrue <ArgumentException>(token.IsValid, nameof(token), "ContinuationToken is invalid.");

            var templates = await this.store.ListTemplatesAsync(account, token, count);

            var response = ServiceProviderResponse.CreateJsonResponse(HttpStatusCode.OK, templates);

            if (templates.NextLink != null)
            {
                response.Headers = new Dictionary <string, IEnumerable <string> >
                {
                    { ContinuationToken.ContinuationTokenKey, new List <string> {
                          templates.NextLink.Token
                      } }
                };
            }

            return(response);
        }
        public async Task <ServiceProviderResponse> GetAccount(
            [FromHeader(Constant.OperationTrackingIdHeader)] string requestId,
            string account)
        {
            var result = await this.GetAccountAsync(account);

            return(ServiceProviderResponse.CreateJsonResponse(HttpStatusCode.OK, result));
        }
Example #5
0
        public async Task <ServiceProviderResponse> GetCredential(
            [FromHeader(Constant.OperationTrackingIdHeader)] string requestId,
            string provider,
            string id)
        {
            var connectorCredential = await this.credentialManager.GetConnectorCredentialByIdAsync(new ConnectorIdentifier(provider, id));

            return(ServiceProviderResponse.CreateJsonResponse(HttpStatusCode.OK, new Credential(connectorCredential)));
        }
        public async Task <ServiceProviderResponse> ListCredentialAssignments(
            [FromHeader(Constant.OperationTrackingIdHeader)] string requestId,
            string account)
        {
            var currentAccount = await EnsureAccount(account, requestId);

            var response = await this.credentialManager.ListCredentialAssignmentsByAccountAsync(currentAccount.EngagementAccount, false);

            return(ServiceProviderResponse.CreateJsonResponse(HttpStatusCode.OK, response));
        }
        public async Task <ServiceProviderResponse> GetInboundMessagesync(
            [FromHeader(Constant.OperationTrackingIdHeader)] string requestId,
            [FromHeader] string account)
        {
            var currentAccount = await this.ValidateAccount(account);

            var messages = await this.inboundManager.GetInboundMessages(account);

            return(ServiceProviderResponse.CreateJsonResponse(HttpStatusCode.OK, messages));
        }
        public async Task <ServiceProviderResponse> CreateOrUpdateAccount(
            [FromHeader(Constant.OperationTrackingIdHeader)] string requestId,
            [FromBody] Account request)
        {
            Validator.ArgumentNotNull(request, nameof(request));
            Validator.ArgumentNotNullOrEmpty(request.EngagementAccount, nameof(request.EngagementAccount));

            var account = await this.CreateOrUpdateAccountAsync(request);

            return(ServiceProviderResponse.CreateJsonResponse(HttpStatusCode.OK, account));
        }
Example #9
0
        public async Task <ServiceProviderResponse> GetTemplateAsync(
            [FromHeader(Constant.OperationTrackingIdHeader)] string requestId,
            [FromHeader] string account,
            string value)
        {
            var currentAccount = await EnsureAccount(account, requestId);

            var template = await this.engine.GetTemplateAsync(currentAccount.EngagementAccount, value, requestId);

            return(ServiceProviderResponse.CreateJsonResponse(HttpStatusCode.OK, template));
        }
        public async Task <ServiceProviderResponse> ListCredentialAssignments(
            [FromHeader(Constant.OperationTrackingIdHeader)] string requestId,
            string account)
        {
            await ValidateAccount(account);

            var assignmentList = await this.credentialManager.ListCredentialAssignmentsByAccountAsync(account, ChannelType.Both, false);

            var response = assignmentList?.Select(a => new CredentialAssignment(a)).ToList();

            return(ServiceProviderResponse.CreateJsonResponse(HttpStatusCode.OK, response));
        }
Example #11
0
        public async Task <ServiceProviderResponse> GetDomainAsync(
            [FromHeader(Constant.OperationTrackingIdHeader)] string requestId,
            [FromHeader] string account,
            string value)
        {
            var currentAccount = await EnsureAccount(account, requestId);

            var domain = await this.store.GetDomainAsync(currentAccount.EngagementAccount, value);

            Validator.IsTrue <ResourceNotFoundException>(domain != null, nameof(domain), "The Domain '{0}' does not exist.", value);

            return(ServiceProviderResponse.CreateJsonResponse(HttpStatusCode.OK, domain));
        }
        public async Task <ServiceProviderResponse> GetSignatureExtensionAsync(
            [FromHeader(Constant.OperationTrackingIdHeader)] string requestId,
            string account,
            string value)
        {
            var currentAccount = await ValidateAccount(account);

            var signature = await this.store.GetSignatureAsync(account, value);

            Validator.IsTrue <ResourceNotFoundException>(signature != null, nameof(signature), "The signature '{0}' does not exist.", value);

            return(ServiceProviderResponse.CreateJsonResponse(HttpStatusCode.OK, new SignatureEx(signature)));
        }
Example #13
0
        public async Task <ServiceProviderResponse> GetTemplateAsync(
            [FromHeader(Constant.OperationTrackingIdHeader)] string requestId,
            [FromHeader] string account,
            string value)
        {
            await ValidateAccount(account);

            var template = await this.store.GetTemplateAsync(account, value);

            Validator.IsTrue <ResourceNotFoundException>(template != null, nameof(template), "The template '{0}' does not exist.", value);

            return(ServiceProviderResponse.CreateJsonResponse(HttpStatusCode.OK, template));
        }
Example #14
0
        public async Task <ServiceProviderResponse> CreateOrUpdateCredential(
            [FromHeader(Constant.OperationTrackingIdHeader)] string requestId,
            [FromBody] Credential request)
        {
            Validator.ArgumentNotNull(request, nameof(request));
            Validator.ArgumentNotNull(request.ConnectorProperties, nameof(request.ConnectorProperties));
            Validator.ArgumentNotNullOrEmpty(request.ConnectorName, nameof(request.ConnectorName));
            Validator.ArgumentNotNullOrEmpty(request.ConnectorKey, nameof(request.ConnectorKey));

            await this.credentialManager.CreateOrUpdateConnectorCredentialAsync(request.ToConnectorCredential());

            return(ServiceProviderResponse.CreateJsonResponse(HttpStatusCode.OK, request));
        }
Example #15
0
        public async Task <ServiceProviderResponse> CreateOrUpdateTemplateAsync(
            [FromHeader(Constant.OperationTrackingIdHeader)] string requestId,
            [FromHeader] string account,
            [FromBody] TemplateCreateOrUpdateRequest request)
        {
            await ValidateAccount(account);

            Validator.ArgumentNotNull(request, nameof(request));
            Validator.ArgumentNotNullOrEmpty(request.Name, request.Name);
            Validator.ArgumentNotNullOrEmpty(request.Signature, request.Signature);
            Validator.IsTrue <ArgumentException>(request.Category != MessageCategory.Invalid, nameof(request.Category), "Invalid message category.");
            ValidateTemplateBody(request.Body, request.Category);

            var signature = await this.store.GetSignatureAsync(account, request.Signature);

            Validator.IsTrue <ResourceNotFoundException>(signature != null, nameof(signature), "The signature '{0}' does not exist.", request.Signature);
            Validator.IsTrue <ArgumentException>(signature.State == ResourceState.Active, nameof(signature), "The signature '{0}' is not active.", request.Signature);
            ValidateSignagureForMessageCategory(signature, request.Category);

            var template = new Template
            {
                EngagementAccount = account,
                Name      = request.Name,
                Signature = request.Signature,
                Category  = request.Category,
                Body      = request.Body,
                State     = ResourceState.Pending
            };

            template = await this.store.CreateOrUpdateTemplateAsync(template);

            // Send ops mail
            if (this.mailHelper != null)
            {
                SmsProviderEventSource.Current.Info(requestId, this, nameof(this.CreateOrUpdateTemplateAsync), OperationStates.Starting, $"Sending ops mail. account={account} template={template.Name}");

                var channelType = SmsConstant.MessageSendChannelMappings[template.Category];
                var assignment  = await this.credentialManager.GetCredentialAssignmentByAccountAsync(account, channelType);

                this.mailHelper.SendMailOnTemplateCreatedOrUpdated(template, assignment != null ? new CredentialAssignment(assignment) : null, requestId);

                SmsProviderEventSource.Current.Info(requestId, this, nameof(this.CreateOrUpdateTemplateAsync), OperationStates.Succeeded, $"Sent ops mail. account={account} template={template.Name}");
            }
            else
            {
                SmsProviderEventSource.Current.Warning(requestId, this, nameof(this.CreateOrUpdateTemplateAsync), OperationStates.Dropped, $"Cannot send ops email because of mailing is not ready. account={account} template={template.Name}");
            }

            return(ServiceProviderResponse.CreateJsonResponse(HttpStatusCode.OK, template));
        }
        public async Task <ServiceProviderResponse> CreateOrUpdateCredentialAssignments(
            [FromHeader(Constant.OperationTrackingIdHeader)] string requestId,
            string account,
            [FromBody] CredentialAssignment request)
        {
            await ValidateAccount(account);

            Validator.ArgumentNotNull(request, nameof(request));
            Validator.ArgumentNotNullOrEmpty(request.Provider, nameof(request.Provider));
            Validator.ArgumentNotNullOrEmpty(request.ConnectorId, nameof(request.ConnectorId));
            Validator.IsTrue <ArgumentException>(request.ChannelType != ChannelType.Invalid, nameof(request.ChannelType), "Invalid channel type.");

            var identifier = new ConnectorIdentifier(request.Provider, request.ConnectorId);
            var credential = await this.credentialManager.GetConnectorCredentialByIdAsync(identifier);

            Validator.IsTrue <ResourceNotFoundException>(credential != null, nameof(credential), "Credential '{0}' does not exist.", identifier);
            Validator.IsTrue <ArgumentException>(credential.ChannelType == request.ChannelType, nameof(request.ChannelType), "Credential '{0}' is for channel type '{1}' but not '{2}'", identifier, credential.ChannelType.ToString(), request.ChannelType.ToString());

            if (string.IsNullOrEmpty(request.ExtendedCode))
            {
                // Auto-assign account code
                var otherAccounts = await this.credentialManager.ListCredentialAssignmentsById(identifier, false);

                if (otherAccounts != null && otherAccounts.Count > 0)
                {
                    var last = otherAccounts.Where(a => a.ExtendedCode != null).OrderBy(a => a.ExtendedCode).LastOrDefault();
                    if (int.TryParse(last.ExtendedCode, out int code) && code < Math.Pow(10, SmsConstant.ExtendedCodeCompanyLength) - 1)
                    {
                        request.ExtendedCode = (code + 1).ToString().PadLeft(SmsConstant.ExtendedCodeCompanyLength, '0');
                    }
                    else
                    {
                        throw new ApplicationException($"Failed the generate new code. The last one is '{last.ExtendedCode}'");
                    }
                }
                else
                {
                    request.ExtendedCode = 1.ToString().PadLeft(SmsConstant.ExtendedCodeCompanyLength, '0');
                }
            }

            await this.credentialManager.CreateOrUpdateCredentialAssignmentAsync(request.ToDataContract(account));

            var assignmentList = await this.credentialManager.ListCredentialAssignmentsByAccountAsync(account, ChannelType.Both, false);

            var response = assignmentList?.Select(a => new CredentialAssignment(a)).ToList();

            return(ServiceProviderResponse.CreateJsonResponse(HttpStatusCode.OK, response));
        }
        public async Task <ServiceProviderResponse> GetMessageDetailAsync(
            [FromHeader(Constant.OperationTrackingIdHeader)] string requestId,
            [FromHeader] string account,
            string messageId)
        {
            var currentAccount = await EnsureAccount(account, requestId);

            var details = await this.reportManager.GetReportAsync(currentAccount.EngagementAccount, messageId);

            Validator.IsTrue <ArgumentException>(details != null, nameof(details), "Message does not exist.");

            var response = ServiceProviderResponse.CreateJsonResponse(HttpStatusCode.OK, details);

            return(response);
        }
        public async Task <ServiceProviderResponse> GetSenderAsync(
            [FromHeader(Constant.OperationTrackingIdHeader)] string requestId,
            [FromHeader] string account,
            System.Guid value,
            [FromQuery] string continuationToken = null,
            [FromQuery] int count = ContinuationToken.DefaultCount)
        {
            Validator.IsTrue <ArgumentException>(count > 0 && count <= EmailConstant.PagingMaxTakeCount, nameof(count), $"Count should be between 0 and {EmailConstant.PagingMaxTakeCount}.");

            var currentAccount = await EnsureAccount(account, requestId);

            var sender = await this.engine.GetSenderAsync(currentAccount.EngagementAccount, value, requestId);

            return(ServiceProviderResponse.CreateJsonResponse(HttpStatusCode.OK, sender));
        }
Example #19
0
        public async Task <ServiceProviderResponse> GetCredential(
            [FromHeader(Constant.OperationTrackingIdHeader)] string requestId,
            string provider,
            string id)
        {
            var connectorCredential = await this.credentialManager.GetConnectorCredentialByIdAsync(new ConnectorIdentifier(provider, id));

            Credential credential = new Credential()
            {
                ConnectorName       = connectorCredential.ConnectorName,
                ConnectorKey        = connectorCredential.ConnectorId,
                ConnectorProperties = new PropertyCollection <string>(connectorCredential.ConnectorProperties)
            };

            return(ServiceProviderResponse.CreateJsonResponse(HttpStatusCode.OK, credential));
        }
        public async Task <ServiceProviderResponse> CreateOrUpdateSignatureAsync(
            [FromHeader(Constant.OperationTrackingIdHeader)] string requestId,
            string account,
            [FromBody] Signature request)
        {
            await ValidateAccount(account);

            Validator.ArgumentNotNull(request, nameof(request));
            Validator.IsTrue <ArgumentException>(request.ChannelType != ChannelType.Invalid, nameof(request.ChannelType), "Invalid channel type.");
            ValidateSignature(request.Value);

            var oldState  = ResourceState.Unknown;
            var signature = await this.store.GetSignatureAsync(account, request.Value);

            if (signature == null)
            {
                // Add restriction that do not create an active signature directly
                // Create a signature with pending status first
                // This is to prevent admin providing incorrect name when activate a signature
                Validator.IsTrue <ArgumentException>(request.State != ResourceState.Active, nameof(request.State), "Cannot create a signature with active state.");

                // Create new with a increased code assigned
                signature = new Signature();
                signature.EngagementAccount = account;
                signature.Value             = request.Value;
                signature.ExtendedCode      = await GetNextExtendedCode(account);
            }

            signature.ChannelType = request.ChannelType;
            signature.State       = request.State;
            signature.Message     = request.Message;

            signature = await this.store.CreateOrUpdateSignatureAsync(signature);

            // Disable all active templates if signature is not in active state, and vice versa
            if (oldState == ResourceState.Active && signature.State != ResourceState.Active)
            {
                await this.store.UpdateTemplateStateBySignatureAsync(account, signature.Value, ResourceState.Active, ResourceState.Disabled, $"Disabled because signature is in state '{signature.State.ToString()}'");
            }
            else if (oldState != ResourceState.Active && signature.State == ResourceState.Active)
            {
                await this.store.UpdateTemplateStateBySignatureAsync(account, signature.Value, ResourceState.Disabled, ResourceState.Active);
            }

            return(ServiceProviderResponse.CreateJsonResponse(HttpStatusCode.OK, new SignatureEx(signature)));
        }
        public async Task <ServiceProviderResponse> SendMessageByGroupAsync(
            [FromHeader(Constant.OperationTrackingIdHeader)] string requestId,
            [FromHeader] string account,
            [FromBody] MessageSendByGroupRequest request)
        {
            Account currentAccount = null;

            try
            {
                // TODO: limit on max target?
                Validator.ArgumentNotNull(request, nameof(request));
                Validator.ArgumentNotNull(request.MessageBody, nameof(request.MessageBody));
                Validator.ArgumentNotNullOrEmpty(request.MessageBody.TemplateName, request.MessageBody.TemplateName);
                Validator.IsTrue <ArgumentException>(request.Targets != null && request.Targets.Count > 0, nameof(request.Targets), "Targets is empty");
                Validator.IsTrue <ArgumentException>(request.Targets.Count <= 10, nameof(request.Targets), "Number of Targets could not be more than 10");

                currentAccount = await EnsureAccount(account, requestId);

                var tuples = await BuildInputMessageAsync(currentAccount, request.MessageBody, request.Targets, TargetType.Group, requestId);

                var message   = tuples.Item1;
                var extension = tuples.Item2;

                // Update report
                await this.reportManager.OnMessageSentAsync(currentAccount.EngagementAccount, message, extension);

                // Dispatch
                await DispatchMessageAsync(message, extension, requestId);

                metricManager.LogSendSuccess(1, currentAccount.EngagementAccount, currentAccount.SubscriptionId ?? string.Empty);

                var response = new MessageSendResponse
                {
                    MessageId = message.MessageInfo.MessageId,
                    SendTime  = message.MessageInfo.SendTime
                };

                return(ServiceProviderResponse.CreateJsonResponse(HttpStatusCode.OK, response));
            }
            catch
            {
                metricManager.LogSendFailed(1, currentAccount.EngagementAccount, currentAccount?.SubscriptionId ?? string.Empty);
                throw;
            }
        }
        public async Task <ServiceProviderResponse> CreateOrUpdateCredentialAssignments(
            [FromHeader(Constant.OperationTrackingIdHeader)] string requestId,
            string account,
            [FromBody] CredentialAssignment request)
        {
            var currentAccount = await EnsureAccount(account, requestId);

            Validator.ArgumentNotNull(request, nameof(request));
            Validator.ArgumentNotNullOrEmpty(request.Provider, nameof(request.Provider));
            Validator.ArgumentNotNullOrEmpty(request.ConnectorId, nameof(request.ConnectorId));

            request.EngagementAccount = currentAccount.EngagementAccount;
            await this.credentialManager.CreateOrUpdateCredentialAssignmentAsync(request);

            var response = await this.credentialManager.ListCredentialAssignmentsByAccountAsync(currentAccount.EngagementAccount, false);

            return(ServiceProviderResponse.CreateJsonResponse(HttpStatusCode.OK, response));
        }
        public async Task <ServiceProviderResponse> GetPerPeriodAggregationAsync(
            [FromHeader(Constant.OperationTrackingIdHeader)] string requestId,
            [FromHeader] string account,
            [FromQuery("startTime")] long startTime = long.MinValue,
            [FromQuery("endTime")] long endTime     = long.MaxValue,
            [FromQuery("resolution")] int seriesResolutionInMinutes = int.MinValue)
        {
            var currentAccount = await this.ValidateAccount(account);

            var utcNow = DateTime.UtcNow;

            try
            {
                DateTimeOffset.FromUnixTimeSeconds(startTime);
            }
            catch
            {
                startTime = new DateTimeOffset(utcNow.Year, utcNow.Month, 1, 0, 0, 0, TimeSpan.Zero).ToUnixTimeSeconds();
            }

            try
            {
                DateTimeOffset.FromUnixTimeSeconds(endTime);
            }
            catch
            {
                endTime = new DateTimeOffset(utcNow).ToUnixTimeSeconds();
            }

            if (seriesResolutionInMinutes < 1)
            {
                seriesResolutionInMinutes = (int)TimeSpan.FromDays(1).TotalMinutes;
            }

            var result = await this.reportManager.GetPerPeriodAggregationAsync(
                requestId,
                account,
                startTime,
                endTime,
                seriesResolutionInMinutes);

            return(ServiceProviderResponse.CreateJsonResponse(HttpStatusCode.OK, result));
        }
        public async Task <ServiceProviderResponse> CreateOrUpdateAccount(
            [FromHeader(Constant.OperationTrackingIdHeader)] string requestId,
            [FromBody] Account request)
        {
            Validator.ArgumentNotNull(request, nameof(request));
            Validator.ArgumentNotNullOrEmpty(request.EngagementAccount, nameof(request.EngagementAccount));
            Validator.ArgumentNotNull(request.AccountSettings, nameof(request.AccountSettings));

            // If provider is configured, verify its existence
            if (!string.IsNullOrEmpty(request.Provider))
            {
                var connector = await this.store.GetConnectorMetadataAsync(request.Provider);

                Validator.IsTrue <ArgumentException>(connector != null, nameof(request.Provider), "Provider '{0}' does not exist.", request.Provider);
            }

            var account = await this.CreateOrUpdateAccountAsync(request);

            return(ServiceProviderResponse.CreateJsonResponse(HttpStatusCode.OK, account));
        }
Example #25
0
        public async Task <ServiceProviderResponse> CreateOrUpdateGroupAsync(
            [FromHeader(Constant.OperationTrackingIdHeader)] string requestId,
            [FromHeader] string account,
            [FromBody] Group request)
        {
            Validator.ArgumentNotNull(request, nameof(request));

            var currentAccount = await EnsureAccount(account, requestId);

            await ValidateDomainExistAsync(currentAccount.EngagementAccount);

            request.EngagementAccount = currentAccount.EngagementAccount;
            var groupResult = await this.engine.CreateOrUpdateGroupAsync(request, requestId);

            if (groupResult.Invalid != null && groupResult.Invalid.Count <= 0)
            {
                groupResult.Invalid = null;
            }

            return(ServiceProviderResponse.CreateJsonResponse(HttpStatusCode.OK, groupResult));
        }
Example #26
0
        public async Task <ServiceProviderResponse> UpdateTemplateAsync(
            [FromHeader(Constant.OperationTrackingIdHeader)] string requestId,
            string account,
            string value,
            [FromBody] TemplateUpdateRequest request)
        {
            await ValidateAccount(account);

            Validator.ArgumentNotNull(request, nameof(request));
            Validator.IsTrue <ArgumentException>(request.State != ResourceState.Unknown, nameof(request.State), "Invalid template state.");

            var template = await this.store.GetTemplateAsync(account, value);

            Validator.IsTrue <ArgumentException>(template != null, nameof(template), "Template '{0}' does not exist.", value);

            template.State   = request.State;
            template.Message = request.Message;

            template = await this.store.CreateOrUpdateTemplateAsync(template);

            return(ServiceProviderResponse.CreateJsonResponse(HttpStatusCode.OK, template));
        }
        public async Task <ServiceProviderResponse> CreateOrUpdateSenderAsync(
            [FromHeader(Constant.OperationTrackingIdHeader)] string requestId,
            [FromHeader] string account,
            [FromBody] Sender request)
        {
            Validator.ArgumentNotNull(request, nameof(request));
            Validator.ArgumentNotNullOrEmpty(request.SenderAddress, nameof(request.SenderAddress));
            Validator.ArgumentNotNullOrEmpty(request.ForwardAddress, nameof(request.ForwardAddress));
            Validator.IsTrue <ArgumentException>(EmailValidator.IsEmailValid(request.SenderAddress), nameof(request.SenderAddress), "SenderAddr is invalid.");
            Validator.IsTrue <ArgumentException>(EmailValidator.IsEmailValid(request.ForwardAddress), nameof(request.ForwardAddress), "ForwardAddr is invalid.");
            if (request.SenderAddrID != null)
            {
                Validator.ArgumentValidGuid(request.SenderAddrID, nameof(request.SenderAddrID));
            }

            var currentAccount = await EnsureAccount(account, requestId);

            request.EngagementAccount = currentAccount.EngagementAccount;
            var senderResult = await this.engine.CreateOrUpdateSenderAsync(request, requestId);

            return(ServiceProviderResponse.CreateJsonResponse(HttpStatusCode.OK, senderResult));
        }
        public async Task <ServiceProviderResponse> GetPerMessageAggregationAsync(
            [FromHeader(Constant.OperationTrackingIdHeader)] string requestId,
            [FromHeader] string account,
            [FromQuery("startTime")] long startTimeUnixSeconds = long.MinValue,
            [FromQuery("endTime")] long endTimeUnixSeconds     = long.MaxValue,
            [FromQuery] string continuationToken = null,
            [FromQuery] int count = ContinuationToken.DefaultCount)
        {
            var currentAccount = await this.ValidateAccount(account);

            Validator.IsTrue <ArgumentException>(count > 0 && count <= SmsConstant.PagingMaxTakeCount, nameof(count), $"Count should be between 0 and {SmsConstant.PagingMaxTakeCount}.");

            var token = new AzureStorageContinuationToken(continuationToken);

            Validator.IsTrue <ArgumentException>(token.IsValid, nameof(token), "ContinuationToken is invalid.");

            var            utcNow = DateTime.UtcNow;
            DateTimeOffset startTime, endTime;

            try
            {
                startTime = DateTimeOffset.FromUnixTimeSeconds(startTimeUnixSeconds);
            }
            catch
            {
                startTime = new DateTimeOffset(utcNow.Year, utcNow.Month, 1, 0, 0, 0, TimeSpan.Zero);
            }

            try
            {
                endTime = DateTimeOffset.FromUnixTimeSeconds(endTimeUnixSeconds);
            }
            catch
            {
                endTime = new DateTimeOffset(utcNow);
            }

            var result = await this.reportManager.GetPerMessageAggregationAsync(
                account,
                startTime,
                endTime,
                count,
                token.TableContinuationToken);

            var response = ServiceProviderResponse.CreateJsonResponse(HttpStatusCode.OK, result);

            if (result.ContinuationToken != null)
            {
                response.Headers = new Dictionary <string, IEnumerable <string> >
                {
                    {
                        ContinuationToken.ContinuationTokenKey,
                        new[]
                        {
                            new AzureStorageContinuationToken(result.ContinuationToken).Token
                        }
                    }
                };
            }

            return(response);
        }
Example #29
0
        public async Task <ServiceProviderResponse> CreateOrUpdateDomainAsync(
            [FromHeader(Constant.OperationTrackingIdHeader)] string requestId,
            string account,
            [FromBody] Domain request)
        {
            Validator.ArgumentNotNull(request, nameof(request));
            ValidateDomain(request.Value);

            var currentAccount = await EnsureAccount(account, requestId);

            var oldState = ResourceState.Unknown;
            var domains  = await this.store.GetDomainsByNameAsync(request.Value);

            if (domains != null)
            {
                // Check if domain is used by other
                Validator.IsTrue <ArgumentException>(
                    !domains.Any(d => d.State == ResourceState.Active && !d.EngagementAccount.Equals(currentAccount.EngagementAccount, StringComparison.OrdinalIgnoreCase)),
                    nameof(domains),
                    "Domain '{0}' is alreay used by other account.",
                    request.Value);
            }

            var domain = domains?.SingleOrDefault(d => d.EngagementAccount.Equals(currentAccount.EngagementAccount, StringComparison.OrdinalIgnoreCase));

            if (domain == null)
            {
                // Add restriction that do not create an active Domain directly
                // Create a Domain with pending status first
                // This is to prevent admin providing incorrect name when activate a Domain
                Validator.IsTrue <ArgumentException>(request.State != ResourceState.Active, nameof(request.State), "Cannot create a domain with active state.");

                domain = new Domain();
                domain.EngagementAccount = currentAccount.EngagementAccount;
                domain.Value             = request.Value;
            }
            else
            {
                oldState = domain.State;
            }

            domain.State   = request.State;
            domain.Message = request.Message;
            domain         = await this.store.CreateOrUpdateDomainAsync(domain);

            // Disable all active templates if Domain is not in active state, and vice versa
            if (oldState == ResourceState.Active && domain.State != ResourceState.Active)
            {
                await this.engine.UpdateTemplateStateByDomainAsync(currentAccount.EngagementAccount, domain.Value, ResourceState.Active, ResourceState.Disabled, $"Disabled because domain is in state '{domain.State.ToString()}'");
            }
            else if (oldState != ResourceState.Active && domain.State == ResourceState.Active)
            {
                try
                {
                    // Ensure EmailAccount exist when setting an active domain
                    await this.credentialManager.EnsureEmailAccountAsync(currentAccount.EngagementAccount);
                }
                catch (Exception ex)
                {
                    EmailProviderEventSource.Current.CriticalException(requestId, this, nameof(this.CreateOrUpdateDomainAsync), OperationStates.Failed, $"Failed to create EmailAccount for account {account}", ex);

                    // Reset domain state to oldState
                    domain.State   = oldState;
                    domain.Message = null;
                    domain         = await this.store.CreateOrUpdateDomainAsync(domain);

                    throw;
                }

                await this.engine.UpdateTemplateStateByDomainAsync(currentAccount.EngagementAccount, domain.Value, ResourceState.Disabled, ResourceState.Active, null);
            }

            return(ServiceProviderResponse.CreateJsonResponse(HttpStatusCode.OK, domain));
        }
        public async Task <ServiceProviderResponse> SendMessageAsync(
            [FromHeader(Constant.OperationTrackingIdHeader)] string requestId,
            [FromHeader] string account,
            [FromBody] MessageSendRequest request)
        {
            Account currentAccount = null;

            try
            {
                SmsProviderEventSource.Current.Info(requestId, this, nameof(this.SendMessageAsync), OperationStates.Received, $"Request is received");

                currentAccount = await this.ValidateAccount(account);

                Validator.ArgumentNotNull(request, nameof(request));
                Validator.ArgumentNotNull(request.MessageBody, nameof(request.MessageBody));
                Validator.ArgumentNotNullOrEmpty(request.MessageBody.TemplateName, request.MessageBody.TemplateName);

                request.Targets = this.ValidatePhoneNumbers(request.Targets);

                var pack = await this.BuildInputMessageAsync(currentAccount, request, requestId);

                var message = pack.InputMessage;
                SmsProviderEventSource.Current.Info(requestId, this, nameof(this.SendMessageAsync), OperationStates.Starting, $"Message is ready. messageId={message.MessageInfo.MessageId}");

                var signatureQuotaName = string.Format(Constant.SmsSignatureMAUNamingTemplate, pack.Signature.Value);
                SmsProviderEventSource.Current.Info(requestId, this, nameof(this.SendMessageAsync), OperationStates.Empty, $"If quota is enabled, request {request.Targets.Count} for account {account}. Otherwise, ignore");
                await QuotaCheckClient.AcquireQuotaAsync(account, SmsConstant.SmsQuotaName, request.Targets.Count);

                await QuotaCheckClient.AcquireQuotaAsync(account, signatureQuotaName, request.Targets.Count);

                try
                {
                    await this.reportManager.OnMessageSentAsync(account, message, pack.Extension);

                    SmsProviderEventSource.Current.Info(requestId, this, nameof(this.SendMessageAsync), OperationStates.Starting, $"Report is updated. messageId={message.MessageInfo.MessageId}");

                    await this.DispatchMessageAsync(message, pack.Extension, requestId);

                    SmsProviderEventSource.Current.Info(requestId, this, nameof(this.SendMessageAsync), OperationStates.Starting, $"Message is dispatched. messageId={message.MessageInfo.MessageId}");

                    this.metricManager.LogSendSuccess(1, account, currentAccount.SubscriptionId ?? string.Empty, pack.Extension.MessageCategory);
                    this.metricManager.LogSendTotal(BillingHelper.GetTotalSegments(message.MessageInfo.MessageBody), account, currentAccount.SubscriptionId ?? string.Empty, pack.Extension.MessageCategory);
                    var response = new MessageSendResponse
                    {
                        MessageId = message.MessageInfo.MessageId,
                        SendTime  = message.MessageInfo.SendTime
                    };

                    return(ServiceProviderResponse.CreateJsonResponse(HttpStatusCode.OK, response));
                }
                catch
                {
                    SmsProviderEventSource.Current.Info(requestId, this, nameof(this.SendMessageAsync), OperationStates.Empty, $"If quota is enabled, release {request.Targets.Count} for account {account}. Otherwise, ignore");
                    await QuotaCheckClient.ReleaseQuotaAsync(account, SmsConstant.SmsQuotaName, request.Targets.Count);

                    await QuotaCheckClient.ReleaseQuotaAsync(account, signatureQuotaName, request.Targets.Count);

                    this.metricManager.LogSendFailed(1, account, currentAccount.SubscriptionId ?? string.Empty, pack.Extension.MessageCategory);
                    throw;
                }
            }
            catch
            {
                this.metricManager.LogSendFailed(1, account, currentAccount?.SubscriptionId ?? string.Empty, null);
                throw;
            }
        }