예제 #1
0
        public async Task TriggerMatchingTests(string registered, int expected)
        {
            var notification = new WebHookNotification {
                TriggerId = "something.interesting.happened"
            };

            var context = new InMemoryContext();

            context.Add(new WebHook
            {
                IsPaused = false,
                Filters  = new List <WebHookFilter>
                {
                    new WebHookFilter
                    {
                        TriggerId = registered
                    }
                }
            });
            context.SaveChanges();
            var protector = new Mock <ISecretProtector>();

            protector.Setup(p => p.Unprotect(It.IsAny <string>())).Returns("http://www.example.org");
            var store = new WebHookStore <InMemoryContext>(context, protector.Object);

            var result = await store.GetApplicableWebHooksAsync(notification);

            Assert.Equal(expected, result.Count);
        }
예제 #2
0
        public async Task NormalScenarioAsync(object payload)
        {
            var logger           = new Mock <ILogger <DefaultWebHookSender> >();
            var signature        = "FIXED_SIGNATURE";
            var signatureService = new Mock <ISignatureService>();

            signatureService.Setup(s => s.GetSignature(It.IsAny <string>(), It.IsAny <string>())).Returns(signature);

            var webHook = new WebHook {
                Callback = "http://www.example.com"
            };
            var notif = new WebHookNotification {
                TriggerId = "noun.verb", Payload = payload
            };

            var callbackHasBeenCalled = false;
            var httpClient            = HttpClientMocker.Callback(async m =>
            {
                callbackHasBeenCalled = true;
                Assert.Equal(HttpMethod.Post, m.Method);
                Assert.Equal(new Uri(webHook.Callback), m.RequestUri);

                var content = JsonConvert.DeserializeObject <Dictionary <string, object> >(await m.Content.ReadAsStringAsync());

                var headers = m.Headers.Select(kvp => kvp.Key).ToHashSet();
                Assert.Contains(DefaultWebHookSender.TimestampKey, headers);
                Assert.Contains(DefaultWebHookSender.UniqueIdKey, headers);

                if (notif.Payload != null)
                {
                    Assert.NotEqual(default, content["notificationId"]);
        public async Task GetAllTests()
        {
            var services = _fixture.Provider;
            var context  = services.GetRequiredService <TestContext1>();

            await SeedAsync(context);

            var store = services.GetRequiredService <WebHookStore <TestContext1> >();

            var payload = new WebHookNotification
            {
                TriggerId = "noun.verb",
                Payload   = new Dictionary <string, object>
                {
                    ["property"]      = 2,
                    ["otherProperty"] = "value",
                    ["thirdProperty"] = "nope"
                }
            };
            var webHooks = await store.GetApplicableWebHooksAsync(payload);

            Assert.Equal(4, webHooks.Count);
            Assert.All(webHooks, t =>
            {
                Assert.NotEqual(Guid.Empty, t.Id);
                Assert.False(t.IsPaused);
            });
            AssertHaveBeenPrepared(webHooks);
        }
예제 #4
0
        public async Task GetAllTests()
        {
            var services = _fixture.Provider;
            var context  = services.GetRequiredService <TestContext1>();

            await SeedAsync(context);

            var store = services.GetRequiredService <WebHookStore <TestContext1> >();

            var payload = new WebHookNotification
            {
                TriggerId = "noun.verb",
                Payload   = new Payload
                {
                    Property      = 2,
                    OtherProperty = "value",
                    ThirdProperty = "nope"
                }
            };
            var webHooks = await store.GetApplicableWebHooksAsync(payload);

            Assert.All(webHooks, t =>
            {
                Assert.NotEqual(Guid.Empty, t.Id);
                Assert.False(t.IsPaused);
                Assert.True(t.Filters != null && t.Filters.Count != 0);
                Assert.Contains("noun.verb", t.Filters.Select(f => f.Trigger));
            });
            AssertHaveBeenPrepared(webHooks);
            Assert.Equal(2, webHooks.Count);
        }
예제 #5
0
        public Task SendMessageAsync(string organizationId, string projectId, string url, SlackMessage message)
        {
            if (String.IsNullOrEmpty(organizationId))
            {
                throw new ArgumentNullException(nameof(organizationId));
            }

            if (String.IsNullOrEmpty(projectId))
            {
                throw new ArgumentNullException(nameof(projectId));
            }

            if (String.IsNullOrEmpty(url))
            {
                throw new ArgumentNullException(nameof(url));
            }

            if (message == null)
            {
                throw new ArgumentNullException(nameof(message));
            }

            var notification = new WebHookNotification {
                OrganizationId = organizationId,
                ProjectId      = projectId,
                Url            = url,
                Type           = WebHookType.Slack,
                Data           = message
            };

            return(_webHookNotificationQueue.EnqueueAsync(notification));
        }
예제 #6
0
        public async Task FullIntegrationMassTransitTests()
        {
            var expectedWebHooksCount = 10;
            var counter  = new HttpClientMocker.CounterHandler();
            var services = new ServiceCollection();

            var store = new Mock <IWebHookStore>();

            store.Setup(s => s.GetApplicableWebHooksAsync(It.IsAny <IWebHookNotification>(), It.IsAny <CancellationToken>())).ReturnsAsync(() => {
                return(Enumerable.Range(0, expectedWebHooksCount).Select(i => new WebHook {
                    Callback = new Uri("http://www.example.org")
                }).ToList());
            });
            services.AddSingleton(store.Object);

            var protector = new Mock <ISignatureService>();

            protector.Setup(p => p.GetSignature(It.IsAny <string>(), It.IsAny <string>())).Returns("secret");
            services.AddSingleton(protector.Object);

            services.AddHarpoon(c => c.UseAllMassTransitDefaults(a => a.AddHttpMessageHandler(() => counter)));
            services.AddMassTransit(p => Bus.Factory.CreateUsingRabbitMq(cfg =>
            {
                var host = cfg.Host(new Uri("rabbitmq://localhost:5672"), hostConfigurator =>
                {
                    hostConfigurator.Username("guest");
                    hostConfigurator.Password("guest");
                });

                cfg.ConfigureNotificationsConsumer(p, "NotificationsFullTestsQueue");
                cfg.ConfigureWebHookWorkItemsConsumer(p, "WebHookWorkItemsFullTestsQueue");
            }), x => x.UseAllMassTransitDefaults());

            var provider = services.BuildServiceProvider();

            var token = new CancellationTokenSource();
            var hosts = provider.GetRequiredService <IEnumerable <IHostedService> >();

            foreach (var host in hosts)
            {
                await host.StartAsync(token.Token);
            }

            var notif = new WebHookNotification {
                TriggerId = "noun.verb"
            };
            await provider.GetRequiredService <IWebHookService>().NotifyAsync(notif);

            await Task.Delay(10000);

            Assert.Equal(expectedWebHooksCount, counter.Counter);
            token.Cancel();
        }
        public override async Task ProcessAsync(EventContext ctx)
        {
            // if they don't have premium features, then we don't need to queue notifications
            if (!ctx.Organization.HasPremiumFeatures)
            {
                return;
            }

            // notifications are disabled or stack is hidden.
            if (ctx.Stack.DisableNotifications || ctx.Stack.IsHidden)
            {
                return;
            }

            if (ShouldQueueNotification(ctx))
            {
                await _notificationQueue.EnqueueAsync(new EventNotificationWorkItem {
                    EventId          = ctx.Event.Id,
                    IsNew            = ctx.IsNew,
                    IsRegression     = ctx.IsRegression,
                    TotalOccurrences = ctx.Stack.TotalOccurrences
                }).AnyContext();
            }

            var webHooks = await _webHookRepository.GetByOrganizationIdOrProjectIdAsync(ctx.Event.OrganizationId, ctx.Event.ProjectId).AnyContext();

            foreach (var hook in webHooks.Documents)
            {
                if (!ShouldCallWebHook(hook, ctx))
                {
                    continue;
                }

                var context      = new WebHookDataContext(hook.Version, ctx.Event, ctx.Organization, ctx.Project, ctx.Stack, ctx.IsNew, ctx.IsRegression);
                var notification = new WebHookNotification {
                    OrganizationId = ctx.Event.OrganizationId,
                    ProjectId      = ctx.Event.ProjectId,
                    WebHookId      = hook.Id,
                    Url            = hook.Url,
                    Type           = WebHookType.General,
                    Data           = await _webHookDataPluginManager.CreateFromEventAsync(context).AnyContext()
                };

                await _webHookNotificationQueue.EnqueueAsync(notification).AnyContext();

                using (_logger.BeginScope(new Dictionary <string, object> {
                    { "Web Hook Notification", notification }
                }))
                    _logger.LogTrace("Web hook queued: project={project} url={Url}", ctx.Event.ProjectId, hook.Url);
            }
        }
예제 #8
0
        public async Task TriggerMatchingTests()
        {
            var notification = new WebHookNotification {
                TriggerId = "something.interesting.happened"
            };

            var context = new InMemoryContext();

            context.Add(new WebHook
            {
                IsPaused = false,
                Filters  = new List <WebHookFilter> {
                    new WebHookFilter {
                        Trigger = "something.interesting.happened"
                    }
                }
            });
            context.Add(new WebHook
            {
                IsPaused = true,
                Filters  = new List <WebHookFilter> {
                    new WebHookFilter {
                        Trigger = "something.interesting.happened"
                    }
                }
            });
            context.SaveChanges();

            var secret    = "http://www.example.org";
            var protector = new Mock <ISecretProtector>();

            protector.Setup(p => p.Unprotect(It.IsAny <string>())).Returns(secret);

            var matcher = new Mock <IWebHookMatcher>();

            matcher.Setup(m => m.Matches(It.IsAny <IWebHook>(), It.IsAny <IWebHookNotification>())).Returns(true);

            var store = new WebHookStore <InMemoryContext>(context, protector.Object, matcher.Object);

            var result = await store.GetApplicableWebHooksAsync(notification);

            Assert.Equal(1, result.Count);

            var webhook = result[0];

            Assert.False(webhook.IsPaused);

            Assert.Equal(secret, webhook.Secret);
            Assert.Equal(new Uri(secret), webhook.Callback);
        }
예제 #9
0
        public async Task NormalScenarioAsync(Dictionary <string, object> payload)
        {
            var logger           = new Mock <ILogger <DefaultWebHookSender> >();
            var signature        = "FIXED_SIGNATURE";
            var signatureService = new Mock <ISignatureService>();

            signatureService.Setup(s => s.GetSignature(It.IsAny <string>(), It.IsAny <string>())).Returns(signature);

            var webHook = new WebHook {
                Callback = new Uri("http://www.example.com")
            };
            var notif = new WebHookNotification {
                TriggerId = "noun.verb", Payload = payload
            };

            var callbackHasBeenCalled = false;
            var httpClient            = HttpClientMocker.Callback(async m =>
            {
                callbackHasBeenCalled = true;
                Assert.Equal(HttpMethod.Post, m.Method);
                Assert.Equal(webHook.Callback, m.RequestUri);

                var content = JsonConvert.DeserializeObject <Dictionary <string, string> >(await m.Content.ReadAsStringAsync());
                Assert.NotNull(content);
                Assert.Contains(DefaultWebHookSender.TriggerKey, content.Keys);
                Assert.Equal(notif.TriggerId, content[DefaultWebHookSender.TriggerKey]);
                Assert.Contains(DefaultWebHookSender.TimestampKey, content.Keys);
                Assert.Contains(DefaultWebHookSender.UniqueIdKey, content.Keys);

                if (notif.Payload != null)
                {
                    foreach (var kvp in notif.Payload)
                    {
                        Assert.Contains(kvp.Key, content.Keys);
                        Assert.Equal(kvp.Value, content[kvp.Key]);
                    }
                }

                Assert.Contains(DefaultWebHookSender.SignatureHeader, m.Headers.Select(kvp => kvp.Key));
                Assert.Equal(signature, m.Headers.GetValues(DefaultWebHookSender.SignatureHeader).First());
            });

            var service = new CounterDefaultWebHookSender(httpClient, signatureService.Object, logger.Object);
            await service.SendAsync(new WebHookWorkItem(notif, webHook), CancellationToken.None);

            Assert.True(callbackHasBeenCalled);
            Assert.Equal(1, service.Successes);
        }
예제 #10
0
        protected async override Task <JobResult> RunInternalAsync(CancellationToken token)
        {
            Log.Trace().Message("Web hook job starting").Write();

            QueueEntry <WebHookNotification> queueEntry = null;

            try {
                queueEntry = _queue.Dequeue();
            } catch (Exception ex) {
                if (!(ex is TimeoutException))
                {
                    Log.Error().Exception(ex).Message("An error occurred while trying to dequeue the next WebHookNotification: {0}", ex.Message).Write();
                    return(JobResult.FromException(ex));
                }
            }
            if (queueEntry == null)
            {
                return(JobResult.Success);
            }

            Log.Trace().Message("Processing WebHookNotification '{0}'.", queueEntry.Id).Write();

            WebHookNotification body = queueEntry.Value;

            Log.Trace().Project(body.ProjectId).Message("Process web hook call: project={0} url={1}", body.ProjectId, body.Url).Write();

            var client = new HttpClient();

            try {
                var result = client.PostAsJson(body.Url, body.Data.ToJson(Formatting.Indented));

                if (result.StatusCode == HttpStatusCode.Gone)
                {
                    _webHookRepository.RemoveByUrl(body.Url);
                    Log.Warn().Project(body.ProjectId).Message("Deleting web hook: org={0} project={1} url={2}", body.OrganizationId, body.ProjectId, body.Url).Write();
                }

                queueEntry.Complete();

                Log.Info().Project(body.ProjectId).Message("Web hook POST complete: status={0} org={1} project={2} url={3}", result.StatusCode, body.OrganizationId, body.ProjectId, body.Url).Write();
            } catch (Exception ex) {
                queueEntry.Abandon();
                return(JobResult.FromException(ex));
            }

            return(JobResult.Success);
        }
예제 #11
0
        public async Task ErrorScenarioAsync()
        {
            var logger    = new Mock <ILogger <DefaultWebHookSender> >();
            var signature = new Mock <ISignatureService>();

            var webHook = new WebHook {
                Callback = new Uri("http://www.example.com")
            };
            var notif = new WebHookNotification {
                TriggerId = "noun.verb"
            };

            var httpClient = HttpClientMocker.AlwaysFail(new Exception());

            var service = new CounterDefaultWebHookSender(httpClient, signature.Object, logger.Object);
            await service.SendAsync(new WebHookWorkItem(notif, webHook), CancellationToken.None);

            Assert.Equal(1, service.Failures);
        }
예제 #12
0
        public async Task NotFoundScenarioAsync(System.Net.HttpStatusCode code)
        {
            var logger    = new Mock <ILogger <DefaultWebHookSender> >();
            var signature = new Mock <ISignatureService>();

            var webHook = new WebHook {
                Callback = new Uri("http://www.example.com")
            };
            var notif = new WebHookNotification {
                TriggerId = "noun.verb"
            };

            var httpClient = HttpClientMocker.Static(code, "");

            var service = new CounterDefaultWebHookSender(httpClient, signature.Object, logger.Object);
            await service.SendAsync(new WebHookWorkItem(notif, webHook), CancellationToken.None);

            Assert.Equal(1, service.NotFounds);
        }
예제 #13
0
        public async Task NotifyFailure(string livenessName, string content)
        {
            foreach (var webHook in _settings.Webhooks)
            {
                if (webHook.Uri == null || !Uri.TryCreate(webHook.Uri, UriKind.Absolute, out Uri webHookUri))
                {
                    _logger.LogWarning($"The web hook notification uri is not stablished or is not an absolute Uri ({webHook.Name}). Set the webhook uri value on BeatPulse setttings.");

                    continue;
                }

                using (var httpClient = new HttpClient())
                {
                    var targetWebHook = new WebHookNotification()
                    {
                        Name    = webHook.Name,
                        Uri     = webHook.Uri,
                        Payload = webHook.Payload
                    };

                    targetWebHook.Payload = targetWebHook.Payload
                                            .Replace(BeatPulseUIKeys.LIVENESS_BOOKMARK, livenessName)
                                            .Replace(BeatPulseUIKeys.FAILURE_BOOKMARK, content);

                    var payload = new StringContent(targetWebHook.Payload, Encoding.UTF8, BeatPulseUIKeys.DEFAULT_RESPONSE_CONTENT_TYPE);

                    try
                    {
                        var response = await httpClient.PostAsync(webHookUri, payload);

                        if (!response.IsSuccessStatusCode)
                        {
                            _logger.LogError($"The failure notification is not executed successfully for {webHook.Name} webhook. The error code is {response.StatusCode}.");
                        }
                    }
                    catch (Exception exception)
                    {
                        _logger.LogError($"The failure notification for {webHook.Name} is not executed successfully.", exception);
                    }
                }
            }
        }
예제 #14
0
        public override async Task ProcessAsync(EventContext ctx)
        {
            // if they don't have premium features, then we don't need to queue notifications
            if (!ctx.Organization.HasPremiumFeatures)
            {
                return;
            }

            if (ShouldQueueNotification(ctx))
            {
                await _notificationQueue.EnqueueAsync(new EventNotificationWorkItem {
                    EventId          = ctx.Event.Id,
                    IsNew            = ctx.IsNew,
                    IsCritical       = ctx.Event.IsCritical(),
                    IsRegression     = ctx.IsRegression,
                    TotalOccurrences = ctx.Stack.TotalOccurrences,
                    ProjectName      = ctx.Project.Name
                }).AnyContext();
            }

            foreach (WebHook hook in (await _webHookRepository.GetByOrganizationIdOrProjectIdAsync(ctx.Event.OrganizationId, ctx.Event.ProjectId).AnyContext()).Documents)
            {
                if (!ShouldCallWebHook(hook, ctx))
                {
                    continue;
                }

                var context      = new WebHookDataContext(hook.Version, ctx.Event, ctx.Organization, ctx.Project, ctx.Stack, ctx.IsNew, ctx.IsRegression);
                var notification = new WebHookNotification {
                    OrganizationId = ctx.Event.OrganizationId,
                    ProjectId      = ctx.Event.ProjectId,
                    WebHookId      = hook.Id,
                    Url            = hook.Url,
                    Data           = await _webHookDataPluginManager.CreateFromEventAsync(context).AnyContext()
                };

                await _webHookNotificationQueue.EnqueueAsync(notification).AnyContext();

                _logger.Trace().Project(ctx.Event.ProjectId).Message("Web hook queued: project={0} url={1}", ctx.Event.ProjectId, hook.Url).Property("Web Hook Notification", notification).Write();
            }
        }
예제 #15
0
        public async Task NotFoundScenarioAsync(System.Net.HttpStatusCode code)
        {
            var logger    = new Mock <ILogger <EFWebHookSender <TestContext1> > >();
            var signature = new Mock <ISignatureService>();

            var context = _fixture.Provider.GetRequiredService <TestContext1>();
            var webHook = AddWebHook(context, Guid.NewGuid(), "myPrincipalxxx", "noun.verb", false, null);

            webHook.Callback = new Uri("http://example.org");
            await context.SaveChangesAsync();

            var notif = new WebHookNotification {
                TriggerId = "noun.verb"
            };

            var httpClient = HttpClientMocker.Static(code, "");

            var service = new EFWebHookSender <TestContext1>(httpClient, signature.Object, logger.Object, context);
            await service.SendAsync(new WebHookWorkItem(notif, webHook), CancellationToken.None);

            Assert.True((await context.WebHooks.FirstAsync(w => w.Id == webHook.Id)).IsPaused);
        }
예제 #16
0
 public static string FirePushNotification([ActivityTrigger] WebHookNotification notification, TraceWriter log)
 {
     log.Info($"TODO: All regions are sync'd - add whatever onward notification you want here! Repository: {notification.Target.Repository}, Tag: {notification.Target.Tag}, Id: {notification.Request.Id}");
     return($"All regions are sync'd!");
 }
예제 #17
0
 public void MatchesTests(List <WebHookFilter> filters, WebHookNotification notif, bool result)
 {
     Assert.Equal(result, new DefaultWebHookMatcher().Matches(new WebHook {
         Filters = filters
     }, notif));
 }
예제 #18
0
 public static string FireTimeoutNotification([ActivityTrigger] WebHookNotification notification, TraceWriter log)
 {
     log.Info($"TODO: Timed out waiting for replication - add whatever onward notification you want here! Repository: {notification.Target.Repository}, Tag: {notification.Target.Tag}, Id: {notification.Request.Id}");
     return($"Timed out");
 }