diff --git a/Messaging/Migrations/20230805002438_MessageSuccess.Designer.cs b/Messaging/Migrations/20230805002438_MessageSuccess.Designer.cs
new file mode 100644
index 00000000..1059bb1a
--- /dev/null
+++ b/Messaging/Migrations/20230805002438_MessageSuccess.Designer.cs
@@ -0,0 +1,105 @@
+//
+using System;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using Models;
+
+#nullable disable
+
+namespace Messaging.Migrations
+{
+ [DbContext(typeof(MessagingContext))]
+ [Migration("20230805002438_MessageSuccess")]
+ partial class MessageSuccess
+ {
+ ///
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder.HasAnnotation("ProductVersion", "7.0.5");
+
+ modelBuilder.Entity("Models.ClientRegistration", b =>
+ {
+ b.Property("ClientRegistrationId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("TEXT");
+
+ b.Property("AsDialed")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("CallbackUrl")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("ClientSecret")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("DateRegistered")
+ .HasColumnType("TEXT");
+
+ b.Property("RegisteredUpstream")
+ .HasColumnType("INTEGER");
+
+ b.Property("UpstreamStatusDescription")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.HasKey("ClientRegistrationId");
+
+ b.ToTable("ClientRegistrations");
+ });
+
+ modelBuilder.Entity("Models.MessageRecord", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("TEXT");
+
+ b.Property("Content")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("DateReceivedUTC")
+ .HasColumnType("TEXT");
+
+ b.Property("From")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("MediaURLs")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("MessageSource")
+ .HasColumnType("INTEGER");
+
+ b.Property("MessageType")
+ .HasColumnType("INTEGER");
+
+ b.Property("RawRequest")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("RawResponse")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("Succeeded")
+ .HasColumnType("INTEGER");
+
+ b.Property("To")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.ToTable("Messages");
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/Messaging/Migrations/20230805002438_MessageSuccess.cs b/Messaging/Migrations/20230805002438_MessageSuccess.cs
new file mode 100644
index 00000000..f0d78f90
--- /dev/null
+++ b/Messaging/Migrations/20230805002438_MessageSuccess.cs
@@ -0,0 +1,29 @@
+using Microsoft.EntityFrameworkCore.Migrations;
+
+#nullable disable
+
+namespace Messaging.Migrations
+{
+ ///
+ public partial class MessageSuccess : Migration
+ {
+ ///
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.AddColumn(
+ name: "Succeeded",
+ table: "Messages",
+ type: "INTEGER",
+ nullable: false,
+ defaultValue: false);
+ }
+
+ ///
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropColumn(
+ name: "Succeeded",
+ table: "Messages");
+ }
+ }
+}
diff --git a/Messaging/Migrations/MessagingContextModelSnapshot.cs b/Messaging/Migrations/MessagingContextModelSnapshot.cs
index ea84bb2b..64861455 100644
--- a/Messaging/Migrations/MessagingContextModelSnapshot.cs
+++ b/Messaging/Migrations/MessagingContextModelSnapshot.cs
@@ -85,6 +85,9 @@ protected override void BuildModel(ModelBuilder modelBuilder)
.IsRequired()
.HasColumnType("TEXT");
+ b.Property("Succeeded")
+ .HasColumnType("INTEGER");
+
b.Property("To")
.IsRequired()
.HasColumnType("TEXT");
diff --git a/Messaging/Program.cs b/Messaging/Program.cs
index 2d264e8e..772f6b4b 100644
--- a/Messaging/Program.cs
+++ b/Messaging/Program.cs
@@ -645,6 +645,30 @@
})
.RequireAuthorization().WithOpenApi(x => new(x) { Summary = "View all sent and received messages.", Description = "This is intended to help you debug problems with message sending and delivery so you can see if it's this API or the upstream vendor that is causing problems." });
+ app.MapGet("/message/all/failed", async Task, NotFound, BadRequest>> (MessagingContext db, DateTime start, DateTime end) =>
+ {
+ try
+ {
+ var messages = await db.Messages.Where(x => !x.Succeeded && x.DateReceivedUTC > start && x.DateReceivedUTC <= end).OrderByDescending(x => x.DateReceivedUTC).ToArrayAsync();
+
+ if (messages is not null && messages.Any())
+ {
+ return TypedResults.Ok(messages);
+ }
+ else
+ {
+ return TypedResults.NotFound($"No failed messages have been recorded between {start} and {end}.");
+ }
+ }
+ catch (Exception ex)
+ {
+ Log.Error(ex.Message);
+ Log.Error(ex.StackTrace ?? "No stack trace found.");
+ return TypedResults.BadRequest(ex.Message);
+ }
+ })
+ .RequireAuthorization().WithOpenApi(x => new(x) { Summary = "View all sent and received messages.", Description = "This is intended to help you debug problems with message sending and delivery so you can see if it's this API or the upstream vendor that is causing problems." });
+
app.MapPost("/message/send", async Task, BadRequest>> ([Microsoft.AspNetCore.Mvc.FromBody] SendMessageRequest message, bool? test, MessagingContext db) =>
{
var toForward = new toForwardOutbound
@@ -683,7 +707,7 @@
MessageSource = MessageSource.Outgoing,
MessageType = message?.MediaURLs.Length > 0 ? MessageType.MMS : MessageType.SMS,
RawRequest = System.Text.Json.JsonSerializer.Serialize(message),
- RawResponse = $"MSISDN {message?.MSISDN} could not be parsed as valid NANP (North American Numbering Plan) number."
+ RawResponse = $"MSISDN {message?.MSISDN} could not be parsed as valid NANP (North American Numbering Plan) number.",
};
db.Messages.Add(record);
@@ -795,7 +819,8 @@
MessageSource = MessageSource.Outgoing,
MessageType = MessageType.MMS,
RawRequest = System.Text.Json.JsonSerializer.Serialize(multipartContent),
- RawResponse = MMSResponse
+ RawResponse = MMSResponse,
+ Succeeded = true
};
db.Messages.Add(record);
@@ -859,7 +884,8 @@
MessageSource = MessageSource.Outgoing,
MessageType = MessageType.SMS,
RawRequest = System.Text.Json.JsonSerializer.Serialize(toForward),
- RawResponse = System.Text.Json.JsonSerializer.Serialize(sendMessage)
+ RawResponse = System.Text.Json.JsonSerializer.Serialize(sendMessage),
+ Succeeded = true
};
db.Messages.Add(record);
@@ -992,6 +1018,14 @@
MessageType = MessageType.MMS,
};
+ MessageRecord messageRecord = new MessageRecord
+ {
+ RawRequest = incomingRequest,
+ DateReceivedUTC = DateTime.UtcNow,
+ MessageSource = MessageSource.Incoming,
+ MessageType = MessageType.MMS,
+ };
+
if (!string.IsNullOrWhiteSpace(msisdn))
{
bool checkFrom = PhoneNumbersNA.PhoneNumber.TryParse(msisdn, out var fromPhoneNumber);
@@ -1065,6 +1099,14 @@
else
{
Log.Error($"Failed to parse To {to} from incoming request {incomingRequest} please file a ticket with the message provider.");
+
+ messageRecord.Content = toForward.Content;
+ messageRecord.From = toForward.From;
+ messageRecord.RawResponse = $"Failed to parse To {to} from incoming request {incomingRequest} please file a ticket with the message provider.";
+
+ db.Messages.Add(messageRecord);
+ await db.SaveChangesAsync();
+
return TypedResults.BadRequest($"To {to}{fullrecipientlist} could not be parsed as valid NANP (North American Numbering Plan) numbers. {incomingRequest}");
}
}
@@ -1101,6 +1143,7 @@
}
}
toForward.MediaURLs = mediaURLs.ToArray();
+ messageRecord.MediaURLs = string.Join(',', toForward.MediaURLs);
// We already know that it's good.
_ = PhoneNumbersNA.PhoneNumber.TryParse(toForward.To, out var toRegisteredNumber);
@@ -1115,41 +1158,51 @@
var response = await client.CallbackUrl.PostJsonAsync(toForward);
Log.Information(await response.GetStringAsync());
Log.Information(System.Text.Json.JsonSerializer.Serialize(toForward));
+ messageRecord.RawResponse = System.Text.Json.JsonSerializer.Serialize(toForward);
+ messageRecord.Succeeded = true;
}
catch (FlurlHttpException ex)
{
- Log.Error(await ex.GetResponseStringAsync());
+ string error = await ex.GetResponseStringAsync();
+ Log.Error(error);
Log.Error(System.Text.Json.JsonSerializer.Serialize(client));
Log.Error(System.Text.Json.JsonSerializer.Serialize(toForward));
Log.Error($"Failed to forward message to {toForward.To}");
+ messageRecord.RawResponse = $"Failed to forward message to {toForward.To} {error}";
}
- MessageRecord record = toForward.ToMessageRecord(incomingRequest, System.Text.Json.JsonSerializer.Serialize(toForward));
-
try
{
- await db.Messages.AddAsync(record);
+ await db.Messages.AddAsync(messageRecord);
await db.SaveChangesAsync();
}
catch (Exception ex)
{
- return TypedResults.BadRequest($"Failed to save incoming message to the database. {ex.Message} {System.Text.Json.JsonSerializer.Serialize(record)}");
+ return TypedResults.BadRequest($"Failed to save incoming message to the database. {ex.Message} {System.Text.Json.JsonSerializer.Serialize(messageRecord)}");
}
- Log.Information(System.Text.Json.JsonSerializer.Serialize(record));
+ Log.Information(System.Text.Json.JsonSerializer.Serialize(messageRecord));
return TypedResults.Ok("The incoming message was received and forwarded to the client.");
}
else
{
Log.Warning($"{toForward.To} is not registered as a client.");
+
+ messageRecord.To = toForward.To;
+ messageRecord.From = toForward.From;
+ messageRecord.RawResponse = $"{toForward.To} is not registered as a client.";
+ db.Messages.Add(messageRecord);
+ await db.SaveChangesAsync();
+
return TypedResults.BadRequest($"{toForward.To} is not registered as a client.");
}
}
catch (Exception ex)
{
Log.Error(ex.Message);
- Log.Error(ex.StackTrace ?? "No stacktrace found.");
+ Log.Error(ex.StackTrace ?? "No stack trace found.");
Log.Information("Failed to read form data by field name.");
+
return TypedResults.BadRequest(ex.Message);
}
@@ -1190,6 +1243,14 @@
MessageType = MessageType.SMS,
};
+ MessageRecord messageRecord = new MessageRecord
+ {
+ RawRequest = incomingRequest,
+ DateReceivedUTC = DateTime.UtcNow,
+ MessageSource = MessageSource.Incoming,
+ MessageType = MessageType.SMS,
+ };
+
if (!string.IsNullOrWhiteSpace(msisdn))
{
bool checkFrom = PhoneNumbersNA.PhoneNumber.TryParse(msisdn, out var fromPhoneNumber);
@@ -1263,6 +1324,14 @@
else
{
Log.Error($"Failed to parse To {to} from incoming request {incomingRequest} please file a ticket with the message provider.");
+
+ messageRecord.Content = toForward.Content;
+ messageRecord.From = toForward.From;
+ messageRecord.RawResponse = $"Failed to parse To {to} from incoming request {incomingRequest} please file a ticket with the message provider.";
+
+ db.Messages.Add(messageRecord);
+ await db.SaveChangesAsync();
+
return TypedResults.BadRequest($"To {to}{fullrecipientlist} could not be parsed as valid NANP (North American Numbering Plan) numbers. {incomingRequest}");
}
}
@@ -1280,40 +1349,48 @@
var response = await client.CallbackUrl.PostJsonAsync(toForward);
Log.Information(await response.GetStringAsync());
Log.Information(System.Text.Json.JsonSerializer.Serialize(toForward));
+ messageRecord.RawResponse = System.Text.Json.JsonSerializer.Serialize(toForward);
+ messageRecord.Succeeded = true;
}
catch (FlurlHttpException ex)
{
- Log.Error(await ex.GetResponseStringAsync());
+ string error = await ex.GetResponseStringAsync();
+ Log.Error(error);
Log.Error(System.Text.Json.JsonSerializer.Serialize(client));
Log.Error(System.Text.Json.JsonSerializer.Serialize(toForward));
Log.Error($"Failed to forward message to {toForward.To}");
+ messageRecord.RawResponse = $"Failed to forward message to {toForward.To} {error}";
}
-
- MessageRecord record = toForward.ToMessageRecord(incomingRequest, System.Text.Json.JsonSerializer.Serialize(toForward));
-
try
{
- await db.Messages.AddAsync(record);
+ await db.Messages.AddAsync(messageRecord);
await db.SaveChangesAsync();
}
catch (Exception ex)
{
- return TypedResults.BadRequest($"Failed to save incoming message to the database. {ex.Message} {System.Text.Json.JsonSerializer.Serialize(record)}");
+ return TypedResults.BadRequest($"Failed to save incoming message to the database. {ex.Message} {System.Text.Json.JsonSerializer.Serialize(messageRecord)}");
}
- Log.Information(System.Text.Json.JsonSerializer.Serialize(record));
+ Log.Information(System.Text.Json.JsonSerializer.Serialize(messageRecord));
return TypedResults.Ok("The incoming message was received and forwarded to the client.");
}
else
{
Log.Warning($"{toForward.To} is not registered as a client.");
+
+ messageRecord.To = toForward.To;
+ messageRecord.From = toForward.From;
+ messageRecord.RawResponse = $"{toForward.To} is not registered as a client.";
+ db.Messages.Add(messageRecord);
+ await db.SaveChangesAsync();
+
return TypedResults.BadRequest($"{toForward.To} is not registered as a client.");
}
}
catch (Exception ex)
{
Log.Error(ex.Message);
- Log.Error(ex.StackTrace ?? "No stacktrace found.");
+ Log.Error(ex.StackTrace ?? "No stack trace found.");
Log.Information("Failed to read form data by field name.");
return TypedResults.BadRequest("Failed to read form data by field name.");
}
@@ -1490,6 +1567,7 @@ public class MessageRecord
public DateTime DateReceivedUTC { get; set; } = DateTime.UtcNow;
public string RawRequest { get; set; } = string.Empty;
public string RawResponse { get; set; } = string.Empty;
+ public bool Succeeded { get; set; } = false;
}
// Format forward to client apps as JSON.