コード例 #1
0
        /// <summary>
        /// Initializes a new instance of the <see cref="SenderMessage"/> as a copy of
        /// the specified <see cref="IReceiverMessage"/>.
        /// </summary>
        /// <param name="receiverMessage">The <see cref="IReceiverMessage"/> to make a copy of.</param>
        /// <param name="validateHeaderValue">
        /// A function that validates header values, returning either the value passed to it
        /// or an equivalent value. If a value is invalid, the function should attempt to
        /// convert it to another type that is valid. If a value cannot be converted, the
        /// function should throw an exception.
        /// </param>
        public SenderMessage(IReceiverMessage receiverMessage, Func <object, object>?validateHeaderValue = null)
        {
            if (receiverMessage is null)
            {
                throw new ArgumentNullException(nameof(receiverMessage));
            }

            _headers = new HeaderDictionary(validateHeaderValue);

            if (receiverMessage.IsBinary())
            {
                InitAsBinary(receiverMessage.BinaryPayload, receiverMessage.IsCompressed(), out _stringPayload, out _binaryPayload);
            }
            else
            {
                InitAsString(receiverMessage.StringPayload, receiverMessage.IsCompressed(), out _stringPayload, out _binaryPayload);
            }

            foreach (var header in receiverMessage.Headers)
            {
                if (header.Key != HeaderNames.MessageId) // Don't copy the message id
                {
                    _headers[header.Key] = header.Value;
                }
            }
        }
コード例 #2
0
        private async Task OnMessageReceived(IReceiverMessage message)
        {
            if (message.Headers.TryGetValue("operation", out string operation))
            {
                if (operation == "create")
                {
                    await Database.CreateAsync(message.StringPayload);

                    await message.AcknowledgeAsync();
                }
                else if (operation == "update")
                {
                    await Database.UpdateAsync(message.StringPayload);

                    await message.AcknowledgeAsync();
                }
                else if (operation == "delete")
                {
                    await Database.DeleteAsync(message.StringPayload);

                    await message.AcknowledgeAsync();
                }
                else
                {
                    // TODO: Send error log - invalid message
                    await message.RejectAsync();
                }
            }
            else
            {
                // TODO: Send error log - invalid message
                await message.RejectAsync();
            }
        }
        private async Task OnMessageReceivedAsync(IReceiverMessage message)
        {
            var newSettings = new Dictionary <string, string>(StringComparer.OrdinalIgnoreCase);

            try
            {
                JsonConvert.PopulateObject(message.StringPayload, newSettings);
            }
            catch
            {
                await message.RejectAsync().ConfigureAwait(false);

                return;
            }

            var changed = Data.Count != newSettings.Count;

            if (!changed)
            {
                foreach (var newSetting in newSettings)
                {
                    if (Data.ContainsKey(newSetting.Key))
                    {
                        if (Data[newSetting.Key] != newSetting.Value)
                        {
                            changed = true;
                            break;
                        }
                    }
                    else
                    {
                        changed = true;
                        break;
                    }
                }
            }

            if (changed)
            {
                IDictionary <string, string> previousData = null;

                if (message.Headers.TryGetValue("RevertAfterMilliseconds", out int milliseconds) && milliseconds > 0)
                {
                    previousData = Data;
                }

                Data = newSettings;
                OnReload();

                if (previousData != null)
                {
                    await message.AcknowledgeAsync().ConfigureAwait(false);
                    await RevertDataAfterDelay(previousData, milliseconds).ConfigureAwait(false);

                    return;
                }
            }

            await message.AcknowledgeAsync().ConfigureAwait(false);
        }
コード例 #4
0
 public void Bind(IReceiverMessage fromReceiverMessage, CloudEvent toCloudEvent)
 {
     if (fromReceiverMessage.Headers.TryGetValue(KafkaKeyHeader, out string?kafkaKey))
     {
         toCloudEvent.Attributes.Remove(KafkaKeyHeader);
         toCloudEvent.SetPartitionKey(kafkaKey);
     }
 }
コード例 #5
0
 /// <summary>
 /// Indicates that the message was successfully processed and should not
 /// be redelivered.
 /// </summary>
 /// <param name="receiverMessage">The message to acknowledge.</param>
 public static Task AcknowledgeAsync(this IReceiverMessage receiverMessage)
 {
     if (receiverMessage is null)
     {
         throw new ArgumentNullException(nameof(receiverMessage));
     }
     return(receiverMessage.AcknowledgeAsync(CancellationToken.None));
 }
コード例 #6
0
        /// <summary>
        /// Initializes a new instance of the <see cref="CloudEvent"/> class and sets its data,
        /// attributes, and headers according to the payload and headers of the <paramref name=
        /// "receiverMessage"/>.
        /// </summary>
        /// <param name="receiverMessage">
        /// The <see cref="IReceiverMessage"/> with headers that map to cloud event attributes.
        /// </param>
        /// <param name="protocolBinding">
        /// The <see cref="IProtocolBinding"/> used to map <see cref="IReceiverMessage"/> headers to
        /// CloudEvent attributes. If <see langword="null"/>, then <see cref="DefaultProtocolBinding"/>
        /// is used instead.
        /// </param>
        public CloudEvent(IReceiverMessage receiverMessage, IProtocolBinding protocolBinding = null)
        {
            if (receiverMessage is null)
            {
                throw new ArgumentNullException(nameof(receiverMessage));
            }

            FromReceiverMessage(receiverMessage, protocolBinding);
        }
コード例 #7
0
        private async Task OnUserEventReceivedAsync(CorrelatedEvent userEvent, IReceiverMessage message)
        {
            Console.WriteLine($"Received user event: '{userEvent.StringData}' with correlation id: {userEvent.CorrelationId}");

            var worker1Event = userEvent.Copy();
            var worker2Event = userEvent.Copy();

            var middle = userEvent.StringData.Length / 2;

            worker1Event.SetData(userEvent.StringData[..middle]);
コード例 #8
0
        /// <summary>
        /// Creates an instance of <typeparamref name="TCloudEvent"/> with properties mapped from
        /// the headers of <paramref name="receiverMessage"/>.
        /// <para>
        /// The <typeparamref name="TCloudEvent"/> type <em>must</em> define a public constructor
        /// with the exact parameters <c>(<see cref="IReceiverMessage"/>, <see cref=
        /// "IProtocolBinding"/>)</c>. A <see cref="MissingMemberException"/> is immediately
        /// thrown if the class does not define such a constructor.
        /// </para>
        /// </summary>
        /// <typeparam name="TCloudEvent">The type of <see cref="CloudEvent"/> to create.</typeparam>
        /// <param name="receiverMessage">
        /// The <see cref="IReceiverMessage"/> to be mapped to the new <typeparamref name="TCloudEvent"/>.
        /// </param>
        /// <param name="protocolBinding">
        /// The <see cref="IProtocolBinding"/> used to map <see cref="IReceiverMessage"/> headers to
        /// CloudEvent attributes.
        /// </param>
        /// <returns>
        /// A new <typeparamref name="TCloudEvent"/> with properties mapped from the headers of the <see cref="IReceiverMessage"/>.
        /// </returns>
        /// <exception cref="ArgumentNullException">
        /// If <paramref name="receiverMessage"/> is <see langword="null"/>.
        /// </exception>
        /// <exception cref="MissingMemberException">
        /// If the <typeparamref name="TCloudEvent"/> class does not define a public constructor
        /// with the exact parameters <c>(<see cref="IReceiverMessage"/>, <see cref=
        /// "IProtocolBinding"/>)</c>.
        /// </exception>
        public static TCloudEvent To <TCloudEvent>(this IReceiverMessage receiverMessage, IProtocolBinding protocolBinding = null)
            where TCloudEvent : CloudEvent
        {
            if (receiverMessage is null)
            {
                throw new ArgumentNullException(nameof(receiverMessage));
            }

            var messageConstructor = _messageConstructors.GetOrAdd(typeof(TCloudEvent), MessageConstructor.Create)
                                     ?? throw MissingReceiverConstructor(typeof(TCloudEvent));

            return((TCloudEvent)messageConstructor.Invoke(receiverMessage, protocolBinding));
        }
コード例 #9
0
        /// <summary>
        /// Gets the Key of the Kafka message, as stored in the <see cref="KafkaKeyHeader"/> header
        /// of the <see cref="IReceiverMessage"/>.
        /// </summary>
        /// <param name="receiverMessage">The <see cref="IReceiverMessage"/>.</param>
        /// <returns>The Key of the Kafka message.</returns>
        public static string GetKafkaKey(this IReceiverMessage receiverMessage)
        {
            if (receiverMessage is null)
            {
                throw new ArgumentNullException(nameof(receiverMessage));
            }

            if (receiverMessage.Headers.TryGetValue(KafkaKeyHeader, out string kafkaKey))
            {
                return(kafkaKey);
            }

            return(null);
        }
コード例 #10
0
            public Task OnMessageReceivedAsync(IReceiver receiver, IReceiverMessage message)
            {
                var source = new TaskCompletionSource <int>();

                try
                {
                    _onMessageReceived(receiver, message);
                    source.SetResult(0);
                }
                catch (Exception ex)
                {
                    source.SetException(ex);
                }

                return(source.Task);
            }
コード例 #11
0
            public Task OnMessageReceivedAsync(IReceiver receiver, IReceiverMessage message)
            {
                var source = new TaskCompletionSource <int>();

                try
                {
                    _onMessageReceived(receiver, message);
                    source.SetResult(0);
                }
#pragma warning disable CA1031 // Do not catch general exception types
                catch (Exception ex)
#pragma warning restore CA1031 // Do not catch general exception types
                {
                    source.SetException(ex);
                }

                return(source.Task);
            }
コード例 #12
0
        private async Task OnMessageReceivedAsync(IReceiverMessage message)
        {
            var newSettings = new Dictionary <string, string>(StringComparer.OrdinalIgnoreCase);

            try
            {
                JsonConvert.PopulateObject(message.StringPayload, newSettings);
            }
            catch
            {
                await message.RejectAsync().ConfigureAwait(false);

                return;
            }

            if (IsChanged(newSettings, message.Headers))
            {
                Data = newSettings;
                OnReload();
            }

            await message.AcknowledgeAsync().ConfigureAwait(false);
        }
コード例 #13
0
 /// <summary>
 /// Indicates that the message was successfully processed and should not
 /// be redelivered.
 /// </summary>
 /// <param name="receiverMessage">The message to acknowledge.</param>
 public static Task AcknowledgeAsync(this IReceiverMessage receiverMessage) =>
 receiverMessage.AcknowledgeAsync(CancellationToken.None);
コード例 #14
0
 public void Bind(IReceiverMessage fromReceiverMessage, CloudEvent toCloudEvent)
 {
 }
コード例 #15
0
 /// <summary>
 /// Gets a value indicating whether the message's payload was sent compressed,
 /// as indicated in the message's headers.
 /// </summary>
 /// <param name="receiverMessage">The source <see cref="IReceiverMessage"/> object.</param>
 /// <returns>Whether the message's payload was sent compressed.</returns>
 public static bool IsCompressed(this IReceiverMessage receiverMessage) =>
 receiverMessage.GetHeaders()
 .TryGetValue(HeaderNames.IsCompressedPayload, out bool isCompressed) &&
 isCompressed;
コード例 #16
0
 /// <summary>
 /// Initializes a new instance of the <see cref="MessageReceivedEventArgs"/> class.
 /// </summary>
 /// <param name="message">The received message.</param>
 public MessageReceivedEventArgs(IReceiverMessage message)
 {
     _message = message;
 }
コード例 #17
0
 public static void Rollback(this IReceiverMessage receiverMessage) =>
 Sync(() => receiverMessage.RollbackAsync());
コード例 #18
0
 /// <summary>
 /// Handles a received message.
 /// <para>
 /// When invoked, this method invokes the <see cref="IMessageHandler.OnMessageReceivedAsync"/>
 /// method of the <see cref="MessageHandler"/> property. It passes the
 /// <see cref="ForwardingReceiver"/> property as the <c>receiver</c> argument and
 /// a new <see cref="ForwardingReceiverMessage"/> decorator as the <c>message</c>
 /// argument.
 /// </para>
 /// </summary>
 /// <param name="receiver">The instance of <see cref="IReceiver"/> that received the message.</param>
 /// <param name="message">The message that was received.</param>
 public Task OnMessageReceivedAsync(IReceiver receiver, IReceiverMessage message) =>
 MessageHandler.OnMessageReceivedAsync(ForwardingReceiver, new ForwardingReceiverMessage(ForwardingReceiver, message));
コード例 #19
0
 /// <summary>
 /// Handles a received message.
 /// <para>
 /// When invoked, this method invokes the <see cref="IMessageHandler.OnMessageReceivedAsync"/>
 /// method of the <see cref="MessageHandler"/> property. It passes the
 /// <see cref="ForwardingReceiver"/> property as the <c>receiver</c> argument and
 /// a new <see cref="ForwardingReceiverMessage"/> decorator as the <c>message</c>
 /// argument.
 /// </para>
 /// </summary>
 /// <param name="receiver">The instance of <see cref="IReceiver"/> that received the message.</param>
 /// <param name="message">The message that was received.</param>
 public Task OnMessageReceivedAsync(IReceiver receiver, IReceiverMessage message) =>
 MessageHandler?.OnMessageReceivedAsync(ForwardingReceiver, new ForwardingReceiverMessage(ForwardingReceiver, message)) ?? Task.CompletedTask;
コード例 #20
0
 /// <summary>
 /// Indicates that the message could not be successfully processed and should
 /// not be redelivered.
 /// </summary>
 /// <param name="receiverMessage">The message to reject.</param>
 public static Task RejectAsync(this IReceiverMessage receiverMessage) =>
 receiverMessage.RejectAsync(CancellationToken.None);
コード例 #21
0
 /// <summary>
 /// Initializes a new instance of the <see cref="PartitionedEvent"/> class and sets its
 /// data, attributes, and headers according to the payload and headers of the <paramref
 /// name="receiverMessage"/>.
 /// </summary>
 /// <param name="receiverMessage">
 /// The <see cref="IReceiverMessage"/> with headers that map to cloud event attributes.
 /// </param>
 /// <param name="protocolBinding">
 /// The <see cref="IProtocolBinding"/> used to map <see cref="IReceiverMessage"/> headers
 /// to CloudEvent attributes. If <see langword="null"/>, then <see cref=
 /// "CloudEvent.DefaultProtocolBinding"/> is used instead.
 /// </param>
 public PartitionedEvent(IReceiverMessage receiverMessage, IProtocolBinding protocolBinding = null)
     : base(receiverMessage, protocolBinding)
 {
 }
コード例 #22
0
 internal ForwardingReceiverMessage(ForwardingReceiver forwardingReceiver, IReceiverMessage message)
 {
     ForwardingReceiver = forwardingReceiver;
     Message            = message;
 }
コード例 #23
0
 /// <summary>
 /// Initializes a new instance of the <see cref="MessageReceivedEventArgs"/> class.
 /// </summary>
 /// <param name="message">The received message.</param>
 public MessageReceivedEventArgs(IReceiverMessage message)
 {
     _message = message;
 }
コード例 #24
0
 public static void Reject(this IReceiverMessage receiverMessage) =>
 Sync(() => receiverMessage.RejectAsync());
コード例 #25
0
 /// <summary>
 /// Gets the binary value of the message. If the implemenation "speaks" string,
 /// <see cref="Encoding.UTF8"/> is used to convert the string message to a byte array.
 /// </summary>
 /// <param name="source">The message from which to obtain a binary value.</param>
 /// <returns>The binary value of the message.</returns>
 public static byte[] GetBinaryValue(this IReceiverMessage source)
 {
     return(source.GetBinaryValue(Encoding.UTF8));
 }
コード例 #26
0
 public static void Acknowledge(this IReceiverMessage receiverMessage) =>
 Sync(() => receiverMessage.AcknowledgeAsync());
コード例 #27
0
 /// <summary>
 /// Gets a header value by key. If the implementation "speaks" binary,
 /// <see cref="Encoding.UTF8"/> is used to convert the binary header to a string.
 /// </summary>
 /// <param name="source">The message from which to obtain a binary value.</param>
 /// <param name="key">The key of the header to retrieve.</param>
 /// <returns>The string value of the header.</returns>
 public static string GetHeaderValue(this IReceiverMessage source, string key)
 {
     return(source.GetHeaderValue(key, Encoding.UTF8));
 }
コード例 #28
0
 /// <summary>
 /// Initializes a new instance of the <see cref="CorrelatedEvent"/> class and sets its
 /// data, attributes, and headers according to the payload and headers of the <paramref
 /// name="receiverMessage"/>.
 /// </summary>
 /// <param name="receiverMessage">
 /// The <see cref="IReceiverMessage"/> with headers that map to cloud event attributes.
 /// </param>
 /// <param name="protocolBinding">
 /// The <see cref="IProtocolBinding"/> used to map <see cref="IReceiverMessage"/> headers to
 /// CloudEvent attributes. If <see langword="null"/>, then <see cref="CloudEvent.DefaultProtocolBinding"/>
 /// is used instead.
 /// </param>
 public CorrelatedEvent(IReceiverMessage receiverMessage, IProtocolBinding protocolBinding = null)
     : base(receiverMessage, protocolBinding)
 {
 }
コード例 #29
0
 public Task DataReceivedAsync(IReceiverMessage message)
 {
     WriteLine(FormatMessage(message.StringPayload));
     return(message.AcknowledgeAsync());
 }
コード例 #30
0
 /// <summary>
 /// Gets the string value of the message. If the implemenation "speaks" binary,
 /// <see cref="Encoding.UTF8"/> is used to convert the binary message to a string.
 /// </summary>
 /// <param name="source">The message from which to obtain a string value.</param>
 /// <returns>The string value of the message.</returns>
 public static string GetStringValue(this IReceiverMessage source)
 {
     return(source.GetStringValue(Encoding.UTF8));
 }
コード例 #31
0
 /// <summary>
 /// Gets the ID of the message, or null if not found in the message's headers.
 /// </summary>
 /// <param name="receiverMessage">The source <see cref="IReceiverMessage"/> object.</param>
 /// <returns>The ID of the message.</returns>
 public static string GetMessageId(this IReceiverMessage receiverMessage) =>
 receiverMessage.GetHeaders()
 .TryGetValue(HeaderNames.MessageId, out string messageId)
             ? messageId
             : null;