Skip to content

Commit

Permalink
Adding health status to providers (#263)
Browse files Browse the repository at this point in the history
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
  • Loading branch information
csharpfritz and github-actions[bot] authored Oct 29, 2023
1 parent cb87184 commit 0576daf
Show file tree
Hide file tree
Showing 15 changed files with 283 additions and 106 deletions.
6 changes: 6 additions & 0 deletions src/TagzApp.Common/ISocialMediaProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ public interface ISocialMediaProvider
/// <returns></returns>
Task<IEnumerable<Content>> GetContentForHashtag(Hashtag tag, DateTimeOffset since);

/// <summary>
/// Report the health of the provider
/// </summary>
/// <returns></returns>
Task<(SocialMediaStatus Status, string Message)> GetHealth();

/// <summary>
/// Start the provider
/// </summary>
Expand Down
12 changes: 12 additions & 0 deletions src/TagzApp.Common/SocialMediaStatus.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace TagzApp.Common;

/// <summary>
/// Defines the health status of a Social Media Provider
/// </summary>
public enum SocialMediaStatus
{
Unknown = -1,
Unhealthy = 0,
Degraded = 1,
Healthy = 2
}
13 changes: 13 additions & 0 deletions src/TagzApp.Providers.Blazot/BlazotProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ internal sealed class BlazotProvider : ISocialMediaProvider
public string Id => BlazotConstants.ProviderId;
public string DisplayName => BlazotConstants.DisplayName;

private SocialMediaStatus _Status = SocialMediaStatus.Unhealthy;
private string _StatusMessage = "Not started";

public string Description { get; init; } = "Blazot is an all new social networking platform and your launchpad to the social universe!";

public BlazotProvider(ILogger<BlazotProvider> logger, BlazotSettings settings,
Expand Down Expand Up @@ -65,12 +68,20 @@ public async Task<IEnumerable<Content>> GetContentForHashtag(Hashtag tag, DateTi

transmissions = await _TransmissionsService.GetHashtagTransmissionsAsync(tag, dateTimeOffset);

_Status = SocialMediaStatus.Healthy;
_StatusMessage = "OK";

if (transmissions == null)
return Enumerable.Empty<Content>();

}
catch (Exception ex)
{
_Logger.LogError(ex, "Error fetching Blazot Hashtag Transmissions: {message}", ex.Message);

_Status = SocialMediaStatus.Unhealthy;
_StatusMessage = $"Error fetching Blazot Hashtag Transmissions: {ex.Message}";

}

return _ContentConverter.ConvertToContent(transmissions, tag);
Expand All @@ -80,4 +91,6 @@ public Task StartAsync()
{
return Task.CompletedTask;
}

public Task<(SocialMediaStatus Status, string Message)> GetHealth() => Task.FromResult((_Status, _StatusMessage));
}
15 changes: 15 additions & 0 deletions src/TagzApp.Providers.Mastodon/MastodonProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ internal class MastodonProvider : ISocialMediaProvider, IHasNewestId

private readonly HttpClient _HttpClient;
private readonly ILogger _Logger;
private SocialMediaStatus _Status = SocialMediaStatus.Unhealthy;
private string _StatusMessage = "Not started";

public MastodonProvider(IHttpClientFactory httpClientFactory, ILogger<MastodonProvider> logger,
MastodonConfiguration configuration)
Expand All @@ -34,6 +36,9 @@ public MastodonProvider(IHttpClientFactory httpClientFactory, ILogger<MastodonPr
public async Task<IEnumerable<Content>> GetContentForHashtag(Hashtag tag, DateTimeOffset since)
{

_Status = SocialMediaStatus.Healthy;
_StatusMessage = "OK";

var targetUri = FormatUri(tag);

Message[]? messages = null;
Expand All @@ -49,12 +54,17 @@ public async Task<IEnumerable<Content>> GetContentForHashtag(Hashtag tag, DateTi
{

_Logger.LogError(ex, "Error getting content from Mastodon");
_Status = SocialMediaStatus.Unhealthy;
_StatusMessage = $"Error getting content from Mastodon: {ex.Message}";

return Enumerable.Empty<Content>();

}

if (messages is null || (!messages?.Any() ?? true))
{
_Status = SocialMediaStatus.Healthy;
_StatusMessage = "No new content found";
return Enumerable.Empty<Content>();
}

Expand Down Expand Up @@ -89,6 +99,11 @@ public async Task<IEnumerable<Content>> GetContentForHashtag(Hashtag tag, DateTi

}

public Task<(SocialMediaStatus Status, string Message)> GetHealth()
{
return Task.FromResult((_Status, _StatusMessage));
}

public Task StartAsync()
{
return Task.CompletedTask;
Expand Down
1 change: 1 addition & 0 deletions src/TagzApp.Providers.TwitchChat/ChatClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ public void Init()

public string ChannelName { get; }
public string ChatBotName { get; }
public bool IsRunning => _ReceiveMessagesThread.IsAlive;

private readonly string _OAuthToken;
private readonly CancellationTokenSource _Shutdown;
Expand Down
3 changes: 3 additions & 0 deletions src/TagzApp.Providers.TwitchChat/IChatClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,7 @@ public interface IChatClient : IDisposable
event EventHandler<NewMessageEventArgs> NewMessage;

void Init();

bool IsRunning { get; }

}
50 changes: 48 additions & 2 deletions src/TagzApp.Providers.TwitchChat/TwitchChatProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ public class TwitchChatProvider : ISocialMediaProvider, IDisposable
public TimeSpan NewContentRetrievalFrequency => TimeSpan.FromSeconds(1);
public string Description { get; init; } = "Twitch is where millions of people come together live every day to chat, interact, and make their own entertainment together.";

private SocialMediaStatus _Status = SocialMediaStatus.Unhealthy;
private string _StatusMessage = "Not started";

private static readonly ConcurrentQueue<Content> _Contents = new();
private static readonly CancellationTokenSource _CancellationTokenSource = new();
private readonly TwitchChatConfiguration _Settings;
Expand Down Expand Up @@ -43,13 +46,24 @@ internal TwitchChatProvider(IOptions<TwitchChatConfiguration> settings, ILogger<
private async Task ListenForMessages(IChatClient chatClient = null)
{

_Status = SocialMediaStatus.Degraded;
_StatusMessage = "Starting TwitchChat client";

var token = _CancellationTokenSource.Token;
_Client = chatClient ?? new ChatClient(_Settings.ChannelName, _Settings.ChatBotName, _Settings.OAuthToken, _Logger);

_Client.NewMessage += async (sender, args) =>
{

var profileUrl = await IdentifyProfilePic(args.UserName);
string profileUrl = string.Empty;
try
{
profileUrl = await IdentifyProfilePic(args.UserName);
}
catch (Exception ex)
{
_Logger.LogError(ex, "Failed to identify profile pic for {UserName}", args.UserName);
}

_Contents.Enqueue(new Content
{
Expand All @@ -70,7 +84,20 @@ private async Task ListenForMessages(IChatClient chatClient = null)
});
};

_Client.Init();
try
{
_Client.Init();
}
catch (Exception ex)
{
_Logger.LogError(ex, "Failed to initialize TwitchChat client");
_Status = SocialMediaStatus.Unhealthy;
_StatusMessage = $"Failed to initialize TwitchChat client: '{ex.Message}'";
return;
}

_Status = SocialMediaStatus.Healthy;
_StatusMessage = "OK";

}

Expand All @@ -82,6 +109,17 @@ private async Task<string> IdentifyProfilePic(string userName)
public Task<IEnumerable<Content>> GetContentForHashtag(Hashtag tag, DateTimeOffset since)
{

if (!_Client.IsRunning)
{

// mark status as unhealthy and return empty list
_Status = SocialMediaStatus.Unhealthy;
_StatusMessage = "TwitchChat client is not running";

return Task.FromResult(Enumerable.Empty<Content>());

}

var messages = _Contents.ToList();
if (messages.Count() == 0) return Task.FromResult(Enumerable.Empty<Content>());

Expand All @@ -91,6 +129,9 @@ public Task<IEnumerable<Content>> GetContentForHashtag(Hashtag tag, DateTimeOffs
_Contents.TryDequeue(out _);
}

_Status = SocialMediaStatus.Healthy;
_StatusMessage = "OK";

messages.ForEach(m => m.HashtagSought = tag.Text);

return Task.FromResult(messages.AsEnumerable());
Expand Down Expand Up @@ -131,4 +172,9 @@ public Task StartAsync()
ListenForMessages();
return Task.CompletedTask;
}

public Task<(SocialMediaStatus Status, string Message)> GetHealth()
{
return Task.FromResult((_Status, _StatusMessage));
}
}
28 changes: 27 additions & 1 deletion src/TagzApp.Providers.Twitter/TwitterProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ public class TwitterProvider : ISocialMediaProvider, IHasNewestId
private const int _SearchMaxResults = 100;
private const string _SearchExpansions = "author_id,attachments.media_keys";

private SocialMediaStatus _Status = SocialMediaStatus.Unhealthy;
private string _StatusMessage = "Not started";

public string Id => "TWITTER";
public string DisplayName => "Twitter";
public TimeSpan NewContentRetrievalFrequency => TimeSpan.FromSeconds(30);
Expand Down Expand Up @@ -48,6 +51,9 @@ public async Task<IEnumerable<Content>> GetContentForHashtag(Common.Models.Hasht
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
{

_Status = SocialMediaStatus.Healthy;
_StatusMessage = "OK";

var tweetQuery = "#" + tag.Text.ToLowerInvariant().TrimStart('#') + " -is:retweet";
var sinceTerm = string.IsNullOrEmpty(NewestId) ? "" : $"&since_id={NewestId}";

Expand All @@ -70,6 +76,9 @@ public async Task<IEnumerable<Content>> GetContentForHashtag(Common.Models.Hasht
else
{

_Status = SocialMediaStatus.Degraded;
_StatusMessage = "Twitter provider is not activated - returning sample tweets";

var assembly = Assembly.GetExecutingAssembly();
var resourceName = "TagzApp.Providers.Twitter.Models.SampleTweets.json.gz";
string sampleJson = string.Empty;
Expand All @@ -87,8 +96,14 @@ public async Task<IEnumerable<Content>> GetContentForHashtag(Common.Models.Hasht
}
catch (Exception ex)
{

Console.WriteLine($"Error retrieving tweets: {ex.Message}");

_Status = SocialMediaStatus.Unhealthy;
_StatusMessage = $"Error retrieving tweets: {ex.Message}";

_Logger.LogError(ex, $"Error retrieving tweets");

}

var outTweets = ConvertToContent(recentTweets, tag);
Expand Down Expand Up @@ -187,8 +202,12 @@ private IEnumerable<Content> ConvertToContent(TwitterData? recentTweets, Common.
}
catch (Exception ex)
{
Console.WriteLine($"Error formatting twee ('{t.text}'): ${ex.Message}");
Console.WriteLine($"Error formatting tweet ('{t.text}'): ${ex.Message}");
_Logger.LogError(ex, $"Error formatting tweet: {t.text}");

_Status = SocialMediaStatus.Degraded;
_StatusMessage = $"Error formatting tweet: {t.text}";

}

}
Expand All @@ -215,4 +234,11 @@ public Task StartAsync()
{
return Task.CompletedTask;
}

public Task<(SocialMediaStatus Status, string Message)> GetHealth()
{

return Task.FromResult((_Status, _StatusMessage));

}
}
24 changes: 24 additions & 0 deletions src/TagzApp.Providers.YouTubeChat/YouTubeChatProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ public class YouTubeChatProvider : ISocialMediaProvider, IDisposable
private bool _DisposedValue;
private string _NextPageToken;

private SocialMediaStatus _Status = SocialMediaStatus.Unhealthy;
private string _StatusMessage = "Not started";

public YouTubeChatProvider(YouTubeChatConfiguration config, IOptions<ApplicationConfiguration> appConfig)
{
_ChatConfig = config;
Expand Down Expand Up @@ -70,9 +73,16 @@ public async Task<IEnumerable<Content>> GetContentForHashtag(Hashtag tag, DateTi
_GoogleException = $"{LiveChatId}:{ex.Message}";
LiveChatId = string.Empty;
}

_Status = SocialMediaStatus.Unhealthy;
_StatusMessage = $"Exception while fetching YouTubeChat: {ex.Message}";

return Enumerable.Empty<Content>();
}

_Status = SocialMediaStatus.Healthy;
_StatusMessage = "OK";

return contents.Items.Select(i => new Content
{
Author = new Creator
Expand Down Expand Up @@ -115,6 +125,10 @@ private async Task<YouTubeService> GetGoogleService()
catch (Exception ex)
{
Console.WriteLine($"Exception while refreshing token: {ex.Message}");

_Status = SocialMediaStatus.Unhealthy;
_StatusMessage = $"Exception while refreshing token: {ex.Message}";

throw;
}
var credential = new UserCredential(flow, "me", token);
Expand All @@ -123,6 +137,10 @@ private async Task<YouTubeService> GetGoogleService()
{
HttpClientInitializer = credential
});

_Status = SocialMediaStatus.Degraded;
_StatusMessage = "Starting YouTubeChat client";

return _Service;
}

Expand Down Expand Up @@ -166,6 +184,10 @@ public async Task<IEnumerable<YouTubeBroadcast>> GetBroadcastsForUser()
{
// GoogleApiException: The service youtube has thrown an exception. HttpStatusCode is Forbidden. The user is not enabled for live streaming.
Console.WriteLine($"Exception while fetching YouTube broadcasts: {ex.Message}");

_Status = SocialMediaStatus.Unhealthy;
_StatusMessage = $"Exception while fetching YouTube broadcasts: {ex.Message}";

return Enumerable.Empty<YouTubeBroadcast>();
}

Expand Down Expand Up @@ -217,6 +239,8 @@ public void Dispose()
GC.SuppressFinalize(this);
}

public Task<(SocialMediaStatus Status, string Message)> GetHealth() => Task.FromResult((_Status, _StatusMessage));

#endregion

}
Expand Down
9 changes: 9 additions & 0 deletions src/TagzApp.Providers.Youtube/YoutubeProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ internal class YoutubeProvider : ISocialMediaProvider
public string DisplayName => "Youtube";
public string Description { get; init; }

private SocialMediaStatus _Status = SocialMediaStatus.Unhealthy;
private string _StatusMessage = "Not started";

public TimeSpan NewContentRetrievalFrequency => TimeSpan.FromSeconds(30);

public YoutubeProvider(YoutubeConfiguration options)
Expand All @@ -35,6 +38,9 @@ public async Task<IEnumerable<Content>> GetContentForHashtag(Hashtag tag, DateTi

var searchListResponse = await searchListRequest.ExecuteAsync();

_Status = SocialMediaStatus.Healthy;
_StatusMessage = "OK";

if (searchListResponse.Items == null || (!searchListResponse.Items?.Any() ?? true))
{
return Enumerable.Empty<Content>();
Expand Down Expand Up @@ -63,4 +69,7 @@ public Task StartAsync()
{
return Task.CompletedTask;
}

public Task<(SocialMediaStatus Status, string Message)> GetHealth() => Task.FromResult((_Status, _StatusMessage));

}
Loading

0 comments on commit 0576daf

Please sign in to comment.