private IEnumerable <GetReleasesListResultItem.AssetItem> getAssets(App app, Release release, string tag)
        {
            return(release.Assets.Where(asset => tag == null || asset.Tag == tag).Select(asset => {
                string hashAlgorithmName = Enum.GetName(typeof(HashAlgorithm), asset.ContentHashAlgorithm).ToLower();

                return new GetReleasesListResultItem.AssetItem {
                    Name = asset.Name,
                    ContentSize = asset.ContentSize,
                    ContentHashAlgorithm = hashAlgorithmName,
                    ContentHash = asset.ContentHash,
                    Tag = asset.Tag,
                    DownloadUrl = _resourceUriGenerator.GetAppArchiveReleaseAssetDownloadUri(app, release, asset)
                };
            }));
        }
        public async Task Handle(ReleasePublishedEvent notification, CancellationToken cancellationToken)
        {
            var release = await _db.Releases.Include(e => e.App).Include(e => e.Assets).SingleOrDefaultAsync(e => e.Uuid == notification.Uuid);

            if (release == null)
            {
                _logger.LogError("Release with UUID {0} was not found");
                return;
            }

            var webhooks = await _db.Webhooks.Where(e => e.AppId == release.AppId && (e.FireOnReleasePublished || e.FireOnPrereleasePublished)).ToArrayAsync();

            foreach (var webhook in webhooks)
            {
                if (!release.Prerelease && !webhook.FireOnReleasePublished)
                {
                    continue;
                }

                if (release.Prerelease && !webhook.FireOnPrereleasePublished)
                {
                    continue;
                }

                var description = release.Prerelease
                    ? "A new experimental release is a available."
                    : "A new stable release is available.";

                var builder = _webhookBuilderFactory.Create(webhook.Type);
                builder.SetTitle(builder.Content().Text($"{release.App.Title} {release.Version}"));
                builder.SetDescription(builder.Content().Text(description));

                if (release.Assets.Any())
                {
                    var name  = builder.Content().Text("Downloads");
                    var value = builder.Content().Lines(release.Assets, (asset, b) => b.Link(asset.Name, _resourceUriGenerator.GetAppArchiveReleaseAssetDownloadUri(release.App, release, asset)));
                    builder.AddField(name, value);
                }

                var data = builder.Build();
                _logger.LogInformation("Webhook: URL = {0}; Data = {1}", webhook.Url, data);

                // TODO: retry on error
                // TODO: log errors in DB
                var client = new HttpClient();

                try {
                    var response = await client.PostAsync(webhook.Url, new StringContent(data, Encoding.UTF8, "application/json"));

                    if (!response.IsSuccessStatusCode)
                    {
                        var responseText = await response.Content.ReadAsStringAsync();

                        _logger.LogError("Webhook with UUID {0} failed with status {1}: {2}", webhook.Uuid, (int)response.StatusCode, responseText);
                    }
                } catch (HttpRequestException ex) {
                    _logger.LogError(ex, "Webhook with UUID {0} failed", webhook.Uuid);
                }
            }
        }