示例#1
0
        /// <summary>
        /// Converts the <see cref="SubscriptionOptionsDto"/> to a <see cref="StanSubscriptionOptions"/>
        /// </summary>
        /// <param name="subscriptionOptions">The <see cref="SubscriptionOptionsDto"/> to convert</param>
        /// <returns>The resulting <see cref="StanSubscriptionOptions"/></returns>
        public static StanSubscriptionOptions ToStanSubscriptionOptions(this SubscriptionOptionsDto subscriptionOptions)
        {
            StanSubscriptionOptions stanSubscriptionOptions = StanSubscriptionOptions.GetDefaultOptions();

            stanSubscriptionOptions.ManualAcks = true;
            if (!string.IsNullOrWhiteSpace(subscriptionOptions.DurableName))
            {
                stanSubscriptionOptions.DurableName = subscriptionOptions.DurableName;
                stanSubscriptionOptions.LeaveOpen   = true;
            }
            if (subscriptionOptions.StreamPosition.HasValue)
            {
                if (subscriptionOptions.StreamPosition.Value == StreamPosition.Start)
                {
                    stanSubscriptionOptions.StartWithLastReceived();
                }
                else if (subscriptionOptions.StreamPosition.Value != StreamPosition.End &&
                         subscriptionOptions.StreamPosition.Value < 0)
                {
                    throw new ArgumentOutOfRangeException();
                }
                else
                {
                    stanSubscriptionOptions.StartAt((ulong)subscriptionOptions.StreamPosition.Value);
                }
            }
            return(stanSubscriptionOptions);
        }
示例#2
0
        /// <inheritdoc/>
        public virtual async Task <IOperationResult <SubscriptionDto> > Handle(CreateSubscriptionCommand command, CancellationToken cancellationToken)
        {
            if (!this.ChannelManager.TryGetChannel(command.Channel, out IChannel channel))
            {
                throw new OperationArgumentException($"Failed to find a channel with the specified name '{command.Channel}'", nameof(command.Channel));
            }
            string subscriptionId = Guid.NewGuid().ToString();
            SubscriptionOptionsDto subscriptionOptions = new SubscriptionOptionsDto()
            {
                Id             = subscriptionId,
                Subject        = command.Subject,
                Type           = command.Type,
                Source         = command.Source?.OriginalString,
                DurableName    = command.Durable ? subscriptionId : null,
                StreamPosition = command.StreamPosition
            };
            await channel.SubscribeAsync(subscriptionOptions, cancellationToken);

            this.SubscriptionManager.RegisterSubscription(subscriptionId, command.Subject, command.Type, command.Source, command.Channel, command.Subscribers);
            return(this.Ok(new SubscriptionDto()
            {
                Id = subscriptionId,
                Channel = command.Channel,
                Subject = command.Subject,
                Type = command.Type,
                Source = command.Source,
                Subscribers = command.Subscribers
            }));
        }
示例#3
0
        /// <summary>
        /// Creates the specified <see cref="Resources.Subscription"/> on the <see cref="Resources.Channel"/> it applies to
        /// </summary>
        /// <param name="subscription">The <see cref="Resources.Subscription"/> to create</param>
        /// <returns>A new awaitable <see cref="Task"/></returns>
        protected virtual async Task SubscribeToChannelAsync(Resources.Subscription subscription)
        {
            if (!this.ChannelManager.TryGetChannel(subscription.Spec.Channel, out IChannel channel))
            {
                return;
            }
            if (string.IsNullOrWhiteSpace(subscription.Spec.Id))
            {
                try
                {
                    this.Logger.LogInformation("Updating the subscription with name '{resourceName}'...", subscription.Name());
                    subscription.Spec.Id = Guid.NewGuid().ToString();
                    await this.KubernetesClient.ReplaceNamespacedCustomObjectAsync(subscription, subscription.ApiGroup(), subscription.ApiGroupVersion(), subscription.Namespace(), SubscriptionDefinition.PLURAL, subscription.Name());

                    this.Logger.LogInformation("The subscription with name '{resourceName}' has been successfully updated", subscription.Name());
                }
                catch (HttpOperationException ex)
                {
                    this.Logger.LogError($"An error occured while updating the status of the CRD '{{resourceKind}}' with name '{{resourceName}}': the server responded with a non-success status code '{{statusCode}}'.{Environment.NewLine}Details: {{responseContent}}", subscription.Kind, subscription.Name(), ex.Response.StatusCode, ex.Response.Content);
                }
            }
            SubscriptionOptionsDto subscriptionOptions = new SubscriptionOptionsDto()
            {
                Id             = subscription.Spec.Id,
                DurableName    = subscription.Spec.IsDurable ? subscription.Spec.Id : null,
                Subject        = subscription.Spec.Subject,
                Source         = subscription.Spec.Source?.OriginalString,
                Type           = subscription.Spec.Type,
                StreamPosition = subscription.Spec.Position
            };
            await channel.SubscribeAsync(subscriptionOptions);

            this.RegisterSubscription(subscription);
        }
示例#4
0
 /// <inheritdoc/>
 public virtual async Task InitializeAsync(CancellationToken cancellationToken = default)
 {
     foreach (KeyValuePair <string, string> entry in this.SubscriptionRegistry)
     {
         SubscriptionOptionsDto subscriptionOptions = JsonConvert.DeserializeObject <SubscriptionOptionsDto>(entry.Value);
         await this.SubscribeAsync(subscriptionOptions, false, cancellationToken);
     }
     await Task.CompletedTask;
 }
示例#5
0
        /// <inheritdoc/>
        public virtual async Task SubscribeAsync(SubscriptionOptionsDto subscriptionOptions, CancellationToken cancellationToken = default)
        {
            this.Logger.LogInformation("Creating a new subscription with id '{subscriptionId}' on channel '{channelName}'...", subscriptionOptions.Id, this.Name);
            string json = JsonConvert.SerializeObject(subscriptionOptions);

            using (HttpResponseMessage response = await this.HttpClient.PostAsync("sub", new StringContent(json, Encoding.UTF8, MediaTypeNames.Application.Json)))
            {
                string responseContent = await response.Content?.ReadAsStringAsync();

                if (!response.IsSuccessStatusCode)
                {
                    this.Logger.LogError($"An error occured while subscribing to the channel '{{channel}}': the remote server responded with a '{{statusCode}}' status code.{Environment.NewLine}Details: {{responseContent}}", this.Name, response.StatusCode, responseContent);
                }
            }
            this.Logger.LogInformation("A new subscription with id '{subscriptionId}' has been succesfully created on channel '{channelName}'", subscriptionOptions.Id, this.Name);
        }
示例#6
0
        /// <summary>
        /// Creates a new subscription
        /// </summary>
        /// <param name="subscriptionOptions">The object used to configure the subscription to create</param>
        /// <param name="persist">A boolean indicating whether or not to persist the subscription to the <see cref="SubscriptionRegistry"/></param>
        /// <param name="cancellationToken">A <see cref="CancellationToken"/></param>
        /// <returns>A new awaitable <see cref="Task"/></returns>
        protected virtual async Task SubscribeAsync(SubscriptionOptionsDto subscriptionOptions, bool persist, CancellationToken cancellationToken = default)
        {
            string subject = this.GetStanSubjectFor(subscriptionOptions.Subject);

            this.Logger.LogInformation("NATSS subject: {subject}", subject);
            IStanSubscription subscription = this.StanConnection.Subscribe(subject, subscriptionOptions.ToStanSubscriptionOptions(), this.CreateEventHandlerFor(subscriptionOptions.Id));

            this.Subscriptions.TryAdd(subscriptionOptions.Id, subscription);
            if (persist)
            {
                lock (this._Lock)
                {
                    this.SubscriptionRegistry.InsertKey(subscriptionOptions.Id, JsonConvert.SerializeObject(subscriptionOptions), false);
                }
            }
            await Task.CompletedTask;
        }
示例#7
0
 /// <summary>
 /// Gets the id of the stream the specified <see cref="SubscriptionOptionsDto"/> applies to
 /// </summary>
 /// <param name="subscriptionOptions">The <see cref="SubscriptionOptionsDto"/> to get the stream id for</param>
 /// <returns>The id of the stream the specified <see cref="SubscriptionOptionsDto"/> applies to</returns>
 protected virtual string GetEventStreamIdFor(SubscriptionOptionsDto subscriptionOptions)
 {
     if (!string.IsNullOrWhiteSpace(subscriptionOptions.Subject))
     {
         return($"$cloudevent-subject-{subscriptionOptions.Subject}");
     }
     else if (!string.IsNullOrWhiteSpace(subscriptionOptions.Type))
     {
         return($"$et-{subscriptionOptions.Type}");
     }
     else if (!string.IsNullOrWhiteSpace(subscriptionOptions.Source))
     {
         return($"$cloudevent-source-{subscriptionOptions.Source}");
     }
     else
     {
         throw new NotSupportedException();
     }
 }
示例#8
0
        /// <summary>
        /// Creates a new subscription
        /// </summary>
        /// <param name="subscriptionOptions">The object used to configure the subscription to create</param>
        /// <param name="persist">A boolean indicating whether or not to persist the subscription to the <see cref="SubscriptionRegistry"/></param>
        /// <param name="cancellationToken">A <see cref="CancellationToken"/></param>
        /// <returns>A new awaitable <see cref="Task"/></returns>
        protected virtual async Task SubscribeAsync(SubscriptionOptionsDto subscriptionOptions, bool persist, CancellationToken cancellationToken = default)
        {
            string streamId = this.GetEventStreamIdFor(subscriptionOptions);
            object subscriptionSource;

            if (string.IsNullOrWhiteSpace(subscriptionOptions.DurableName))
            {
                if (subscriptionOptions.StreamPosition.HasValue)
                {
                    CatchUpSubscriptionSettings settings = subscriptionOptions.ToCatchUpSubscriptionSettings();
                    subscriptionSource = this.EventStoreConnection.SubscribeToStreamFrom(streamId, subscriptionOptions.StreamPosition.Value, settings,
                                                                                         this.CreateCatchUpSubscriptionHandler(subscriptionOptions.Id), subscriptionDropped: this.CreateCatchUpSubscriptionDropHandler(subscriptionOptions.Id, streamId, subscriptionOptions.StreamPosition, settings));
                }
                else
                {
                    subscriptionSource = await this.EventStoreConnection.SubscribeToStreamAsync(streamId, true, this.CreateStandardSubscriptionHandler(subscriptionOptions.Id),
                                                                                                subscriptionDropped : this.CreateStandardSubscriptionDropHandler(subscriptionOptions.Id, streamId));
                }
            }
            else
            {
                try
                {
                    await this.EventStoreConnection.CreatePersistentSubscriptionAsync(streamId, subscriptionOptions.DurableName, subscriptionOptions.ToPersistentSubscriptionSettings(),
                                                                                      new UserCredentials(this.ApplicationOptions.EventStore.Username, this.ApplicationOptions.EventStore.Password));
                }
                catch { }
                subscriptionSource = await this.EventStoreConnection.ConnectToPersistentSubscriptionAsync(streamId, subscriptionOptions.DurableName, this.CreatePersistentSubscriptionHandler(subscriptionOptions.Id),
                                                                                                          this.CreatePersistentSubscriptionDropHandler(subscriptionOptions.Id, streamId, subscriptionOptions.DurableName), autoAck : false);
            }
            this.AddOrUpdateSubscription(subscriptionOptions.Id, streamId, subscriptionOptions.DurableName, subscriptionSource);
            if (persist)
            {
                lock (this._Lock)
                {
                    this.SubscriptionRegistry.InsertKey(subscriptionOptions.Id, JsonConvert.SerializeObject(subscriptionOptions), false);
                }
            }
            this.Logger.LogInformation("Created a new EventStore subscription to stream '{streamId}'", streamId);
        }
示例#9
0
 public async Task <IActionResult> Subscribe([FromBody] SubscriptionOptionsDto subscription)
 {
     return(this.Process(await this.Mediator.Send(new SubscribeCommand(subscription)), (int)HttpStatusCode.Accepted));
 }
示例#10
0
 /// <summary>
 /// Initializes a new <see cref="SubscribeCommand"/>
 /// </summary>
 /// <param name="subscriptionOptions">The options used to configure the subscription to create</param>
 public SubscribeCommand(SubscriptionOptionsDto subscriptionOptions)
 {
     this.SubscriptionOptions = subscriptionOptions;
 }
示例#11
0
        /// <summary>
        /// Transforms the <see cref="SubscriptionOptionsDto"/> into a new <see cref="CatchUpSubscriptionSettings"/>
        /// </summary>
        /// <param name="subscriptionOptions">The <see cref="SubscriptionOptionsDto"/> to transform</param>
        /// <returns>A new <see cref="CatchUpSubscriptionSettings"/></returns>
        public static CatchUpSubscriptionSettings ToCatchUpSubscriptionSettings(this SubscriptionOptionsDto subscriptionOptions)
        {
            CatchUpSubscriptionSettings defaultSettings = CatchUpSubscriptionSettings.Default;

            return(new CatchUpSubscriptionSettings(defaultSettings.MaxLiveQueueSize, defaultSettings.ReadBatchSize, defaultSettings.VerboseLogging, true));
        }
示例#12
0
        /// <summary>
        /// Transforms the <see cref="SubscriptionOptionsDto"/> into a new <see cref="PersistentSubscriptionSettings"/>
        /// </summary>
        /// <param name="subscriptionOptions">The <see cref="SubscriptionOptionsDto"/> to transform</param>
        /// <returns>A new <see cref="PersistentSubscriptionSettings"/></returns>
        public static PersistentSubscriptionSettings ToPersistentSubscriptionSettings(this SubscriptionOptionsDto subscriptionOptions)
        {
            PersistentSubscriptionSettingsBuilder builder = PersistentSubscriptionSettings.Create();

            if (subscriptionOptions.StreamPosition.HasValue)
            {
                switch (subscriptionOptions.StreamPosition.Value)
                {
                case StreamPosition.Start:
                    builder.StartFromBeginning();
                    break;

                case StreamPosition.End:
                    builder.StartFromCurrent();
                    break;

                default:
                    builder.StartFrom(subscriptionOptions.StreamPosition.Value);
                    break;
                }
            }
            builder.ResolveLinkTos();
            builder.WithMaxSubscriberCountOf(1);
            builder.MinimumCheckPointCountOf(1);
            builder.MaximumCheckPointCountOf(1);
            return(builder.Build());
        }
示例#13
0
 /// <inheritdoc/>
 public virtual async Task SubscribeAsync(SubscriptionOptionsDto subscriptionOptions, CancellationToken cancellationToken = default)
 {
     await this.SubscribeAsync(subscriptionOptions, true, cancellationToken);
 }