public static void AssertOutboxItemMatchesReceivedItem(
            ISqlTransactionalOutboxItem <Guid> outboxItem,
            ISqlTransactionalOutboxReceivedItem <Guid, string> receivedItem
            )
        {
            Assert.IsNotNull(outboxItem);
            Assert.IsNotNull(receivedItem);

            Assert.AreEqual(outboxItem.UniqueIdentifier, receivedItem.UniqueIdentifier);
            Assert.AreEqual(outboxItem.FifoGroupingIdentifier, receivedItem.FifoGroupingIdentifier);
            Assert.AreEqual(OutboxReceivedItemProcessingStatus.AcknowledgeSuccessfulReceipt, receivedItem.Status);

            var publishedItem = receivedItem.PublishedItem;

            //Received Items should always be Successful
            Assert.AreEqual(OutboxItemStatus.Successful, publishedItem.Status);
            Assert.AreEqual(outboxItem.UniqueIdentifier, publishedItem.UniqueIdentifier);
            Assert.AreEqual(outboxItem.FifoGroupingIdentifier, publishedItem.FifoGroupingIdentifier);
            Assert.AreEqual(outboxItem.PublishTarget, publishedItem.PublishTarget);
            //Publishing process should increment the PublishAttempt by 1
            Assert.AreEqual(outboxItem.PublishAttempts + 1, publishedItem.PublishAttempts);

            //Extract the Body from the original OutboxItem to compare (apples to apples) to the received item from
            //  being published where the Payload would be automatically populated from the Body property mapping.
            var payloadBuilder = PayloadBuilder.FromJsonSafely(outboxItem.Payload);

            Assert.AreEqual(payloadBuilder.Body, publishedItem.Payload);

            //Dates need to be compared at the Millisecond level due to minute differences in Ticks when re-parsing(?)...
            Assert.AreEqual(0,
                            (int)(publishedItem.CreatedDateTimeUtc - outboxItem.CreatedDateTimeUtc).TotalMilliseconds,
                            "The Created Dates differ at the Millisecond level"
                            );
        }
        /// <summary>
        /// Convenience method for adding a completely dynamic JSON payload item easily to the Outbox. Using this approach
        /// is highly flexible as the payload can control the Outbox and publishing process in very dynamic way;
        /// any failed validation of required fields will result in exceptions.
        /// This will parse and process the json payload with the Transactional Outbox using Default implementations (e.g. GUID identifier).
        /// This method will create and commit the Transaction automatically, but may error if a running transaction
        /// is already in progress in which case the custom extension of the Transaction should be used directly instead.
        /// </summary>
        /// <param name="sqlConnection"></param>
        /// <param name="jsonText"></param>
        /// <param name="publishTopic"></param>
        /// <param name="fifoGroupingIdentifier"></param>
        /// <returns></returns>
        public static async Task <ISqlTransactionalOutboxItem <Guid> > AddTransactionalOutboxPendingItemAsync(
            this SqlConnection sqlConnection,
            string jsonText,
            string publishTopic           = null,
            string fifoGroupingIdentifier = null
            )
        {
            var payloadBuilder = PayloadBuilder.FromJsonSafely(jsonText);

            //Publishing Target may be defined in the Payload OR as a discrete parameter that overrides the payload,
            //  but it is REQUIRED!
            var publishingTarget = publishTopic ?? payloadBuilder.PublishTarget;

            publishingTarget.AssertNotNullOrWhiteSpace(nameof(payloadBuilder.PublishTarget), "No Publishing Topic was defined in the Payload or as a parameter.");

            //FIFO Grouping Identifier may be defined in the Payload OR as a discrete parameter that overrides the payload,
            //  but it is OPTIONAL.
            var fifoGroupId = fifoGroupingIdentifier ?? payloadBuilder.FifoGroupingId;

            var results = await sqlConnection
                          .AddTransactionalOutboxPendingItemAsync(
                publishingTarget,
                payloadBuilder.ToJObject(),
                fifoGroupId
                )
                          .ConfigureAwait(false);

            return(results);
        }
        public void TestPayloadBuilderFromJsonToJObject()
        {
            var jsonText = $@"
                {{
                    ""publishTopic"": ""{TestConfiguration.AzureServiceBusTopic}"",
                    ""fifoGroupingId"": ""HttpProxy-IntegrationTest"",
                    ""to"": ""CajunCoding"",
                    ""body"": ""Testing Json Payload from HttpProxy""
                }}            
            ";

            var payloadBuilder = PayloadBuilder.FromJsonSafely(jsonText);
            var jsonPayload    = payloadBuilder.ToJObject();

            Assert.AreEqual(TestConfiguration.AzureServiceBusTopic, jsonPayload.ValueSafely <string>(nameof(PayloadBuilder.PublishTarget)));
            Assert.AreEqual("HttpProxy-IntegrationTest", jsonPayload.ValueSafely <string>(nameof(PayloadBuilder.FifoGroupingId)));
            Assert.AreEqual("CajunCoding", jsonPayload.ValueSafely <string>(nameof(PayloadBuilder.To)));
            Assert.AreEqual("Testing Json Payload from HttpProxy", jsonPayload.ValueSafely <string>(nameof(PayloadBuilder.Body)));
        }
        public void TestPayloadBuilderFromJsonWithPlainTextBody()
        {
            //TODO: Add ALL Field options...
            var jsonText = $@"
                {{
                    ""topic"": ""{TestConfiguration.AzureServiceBusTopic}"",
                    ""fifoGroupingId"": ""HttpProxy-IntegrationTest"",
                    ""to"": ""CajunCoding"",
                    ""body"": ""Testing Json Payload from HttpProxy""
                }}            
            ";

            var payloadBuilder = PayloadBuilder.FromJsonSafely(jsonText);

            Assert.AreEqual(TestConfiguration.AzureServiceBusTopic, payloadBuilder.PublishTarget);
            Assert.AreEqual("HttpProxy-IntegrationTest", payloadBuilder.FifoGroupingId);
            Assert.AreEqual("CajunCoding", payloadBuilder.To);
            Assert.AreEqual("Testing Json Payload from HttpProxy", payloadBuilder.Body);
        }
        public async Task <IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
            ILogger log)
        {
            log.LogInformation($"HTTP [{nameof(TransactionalOutboxHttpProxySendPayloadFunction)}].");
            var configSettings = new SampleAppConfig();

            //Initialize the Payload from the Body as Json!
            var jsonText = await req.ReadAsStringAsync();

            var payloadBuilder = PayloadBuilder.FromJsonSafely(jsonText);

            //Apply fallback values from the QueryString
            //NOTE: this will only set values not already initialized from Json!
            var queryLookup = req.Query.ToLookup(k => k.Key, v => v.Value.FirstOrDefault());

            payloadBuilder.ApplyValues(queryLookup, false);

            await using var sqlConnection = new SqlConnection(configSettings.SqlConnectionString);
            await sqlConnection.OpenAsync();

            //************************************************************
            //*** Add The Payload to our Outbox
            //************************************************************
            var outboxItem = await sqlConnection.AddTransactionalOutboxPendingItemAsync(
                publishTarget : payloadBuilder.PublishTarget,
                payload : payloadBuilder.ToJObject(),
                //It's always a good idea to ensure that a FIFO Group Id/Name is specified for any FIFO Subscriptions that may receive the messages...
                fifoGroupingIdentifier : payloadBuilder.FifoGroupingId ?? "DefaultFifoGroup"
                ).ConfigureAwait(false);

            //Log results and return response to the client...
            log.LogDebug($"Payload:{Environment.NewLine}{outboxItem.Payload}");

            return(new ContentResult()
            {
                Content = outboxItem.Payload,
                ContentType = MessageContentTypes.Json,
                StatusCode = (int)HttpStatusCode.OK
            });
        }