config can be made and articles can be listed in a basic way

This commit is contained in:
James Tombleson 2023-06-04 12:21:44 -07:00
parent 2e17af3b97
commit fe3f4cfae1
30 changed files with 546 additions and 2 deletions

35
Newsbot.Cli.sln Normal file
View File

@ -0,0 +1,35 @@

Microsoft Visual Studio Solution File, Format Version 12.00
#
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Newsbot.Cli", "Newsbot.Cli\Newsbot.Cli.csproj", "{1566F59D-6BFA-4F98-B4B0-F2A5D92B182B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Newsbot.Domain", "Newsbot.Domain\Newsbot.Domain.csproj", "{71C391A5-9A94-449C-896E-8D6AB79CE44B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Newsbot.Services", "Newsbot.Services\Newsbot.Services.csproj", "{B82F77EA-64FF-40AD-BD8B-6EAFAB2062BA}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Newsbot.Tests", "Newsbot.Tests\Newsbot.Tests.csproj", "{04B65EE4-E65C-4AA6-A27C-9400BE74FC31}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{1566F59D-6BFA-4F98-B4B0-F2A5D92B182B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1566F59D-6BFA-4F98-B4B0-F2A5D92B182B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1566F59D-6BFA-4F98-B4B0-F2A5D92B182B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1566F59D-6BFA-4F98-B4B0-F2A5D92B182B}.Release|Any CPU.Build.0 = Release|Any CPU
{71C391A5-9A94-449C-896E-8D6AB79CE44B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{71C391A5-9A94-449C-896E-8D6AB79CE44B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{71C391A5-9A94-449C-896E-8D6AB79CE44B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{71C391A5-9A94-449C-896E-8D6AB79CE44B}.Release|Any CPU.Build.0 = Release|Any CPU
{B82F77EA-64FF-40AD-BD8B-6EAFAB2062BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B82F77EA-64FF-40AD-BD8B-6EAFAB2062BA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B82F77EA-64FF-40AD-BD8B-6EAFAB2062BA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B82F77EA-64FF-40AD-BD8B-6EAFAB2062BA}.Release|Any CPU.Build.0 = Release|Any CPU
{04B65EE4-E65C-4AA6-A27C-9400BE74FC31}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{04B65EE4-E65C-4AA6-A27C-9400BE74FC31}.Debug|Any CPU.Build.0 = Debug|Any CPU
{04B65EE4-E65C-4AA6-A27C-9400BE74FC31}.Release|Any CPU.ActiveCfg = Release|Any CPU
{04B65EE4-E65C-4AA6-A27C-9400BE74FC31}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,35 @@
using System.Runtime.CompilerServices;
using CliFx;
using CliFx.Attributes;
using CliFx.Infrastructure;
using jtom38.Newsbot.Services;
using jtom38.Newsbot.Services.Terminal;
using Newsbot.Services.Collector;
namespace jtom38.Newsbot.Cli.Commands;
[Command("articles list", Description = "List the newest articles")]
public class ArticlesListCommand : ICommand
{
public async ValueTask ExecuteAsync(IConsole console)
{
var cfg = ConfigClient.LoadAsync();
var client = new ArticlesClient(cfg.Result.Uri);
var r = await client.ListArticles();
var count = 0;
foreach (var item in r)
{
var title = item.Title ?? "";
title = title.ReplaceLineEndings("");
var msg = $"[{count}] = {title}";
await console.Output.WriteLineAsync(msg);
count++;
}
//var resp = UserInputService.WaitForInput("Enter the number to read the article");
}
}

View File

@ -0,0 +1,15 @@
using CliFx;
using CliFx.Attributes;
using CliFx.Infrastructure;
namespace jtom38.Newsbot.Cli.Commands.Config;
[Command("config set token")]
public class SetAuthKey : ICommand
{
public ValueTask ExecuteAsync(IConsole console)
{
//await SetInstanceService.Save(Uri);
return default;
}
}

View File

@ -0,0 +1,18 @@
using CliFx;
using CliFx.Attributes;
using CliFx.Infrastructure;
using jtom38.Newsbot.Services.Config;
namespace jtom38.Newsbot.Cli.Commands.Config;
[Command("config set instance")]
public class SetInstance: ICommand
{
[CommandParameter(0, Description = "URI of the instance", Name = "uri")]
public required string Uri { get; set; }
public async ValueTask ExecuteAsync(IConsole console)
{
await SetInstanceService.Save(Uri);
}
}

View File

@ -0,0 +1,16 @@
using CliFx;
using CliFx.Attributes;
using CliFx.Infrastructure;
using jtom38.Newsbot.Services;
namespace jtom38.Newsbot.Cli.Commands.Config;
[Command("config show instance")]
public class ShowInstance : ICommand
{
public async ValueTask ExecuteAsync(IConsole console)
{
var res = await ConfigClient.LoadAsync();
await console.Output.WriteLineAsync(res.Uri);
}
}

View File

@ -0,0 +1,14 @@
using CliFx;
using CliFx.Attributes;
using CliFx.Infrastructure;
namespace jtom38.Newsbot.Cli.Commands;
[Command("sources list", Description = "Lists the known sources")]
public class SourcesList : ICommand
{
public async ValueTask ExecuteAsync(IConsole console)
{
}
}

BIN
Newsbot.Cli/Newsbot.Cli Executable file

Binary file not shown.

View File

@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<PublishSingleFile>true</PublishSingleFile>
<SelfContained>true</SelfContained>
<RootNamespace>jtom38.Newsbot.Cli</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="clifx" Version="2.3.4" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Newsbot.Services\Newsbot.Services.csproj" />
</ItemGroup>
</Project>

7
Newsbot.Cli/Program.cs Normal file
View File

@ -0,0 +1,7 @@
using CliFx;
await new CliApplicationBuilder()
.AddCommandsFromThisAssembly()
.SetExecutableName("Newsbot.Cli")
.Build()
.RunAsync();

View File

@ -0,0 +1,20 @@
namespace jtom38.Newsbot.Domain.Models.Collector;
public class ArticleDetailsDto
{
public Guid ID { get; set; }
public string[]? Tags { get; set; }
public string? Title { get; set; }
public string? Url { get; set; }
public DateTime PubDate { get; set; }
public string? Video { get; set; }
public int VideoHeight { get; set; }
public int VideoWidth { get; set; }
public string? Thumbnail { get; set; }
public string? Description { get; set; }
public string? AuthorName { get; set; }
public string? AuthorImage { get; set; }
public SourceDto? Source { get; set; }
}

View File

@ -0,0 +1,45 @@
using Newtonsoft.Json;
namespace jtom38.Newsbot.Domain.Models.Collector;
public class ArticlesDto
{
[JsonProperty("id")]
public Guid Id { get; set; }
[JsonProperty("sourceId")]
public Guid SourceId { get; set; }
[JsonProperty("tags")]
public List<string>? Tags { get; set; }
[JsonProperty("title")]
public string? Title { get; set; }
[JsonProperty("url")]
public string? Url { get; set; }
[JsonProperty("pubDate")]
public DateTime PubDate { get; set; }
[JsonProperty("video")]
public string? Video { get; set; }
[JsonProperty("videoHeight")]
public int VideoHeight { get; set; }
[JsonProperty("videoWidth")]
public int VideoWidth { get; set; }
[JsonProperty("thumbnail")]
public string? Thumbnail { get; set; }
[JsonProperty("description")]
public string? Description { get; set; }
[JsonProperty("authorName")]
public string? AuthorName { get; set; }
[JsonProperty("authorImage")]
public string? AuthorImage { get; set; }
}

View File

@ -0,0 +1,10 @@
namespace jtom38.Newsbot.Domain.Models.Collector;
public class DiscordWebHookDto
{
public Guid ID { get; set; }
public string? Url { get; set; }
public string? Server { get; set; }
public string? Channel { get; set; }
public bool Enabled { get; set; }
}

View File

@ -0,0 +1,15 @@
namespace jtom38.Newsbot.Domain.Models.Collector;
public class SourceDto
{
public Guid ID { get; set; }
public string? Site { get; set; }
public string? Name { get; set; }
public string? Source { get; set; }
public string? Type { get; set; }
public string? Value { get; set; }
public bool Enabled { get; set; }
public string? Url { get; set; }
public string[]? Tags { get; set; }
public bool Deleted { get; set; }
}

View File

@ -0,0 +1,11 @@
namespace jtom38.Newsbot.Domain.Models.Collector;
public class SubscriptionDetailsDto
{
public Guid Id { get; set; }
public bool CodeAllowReleases { get; set; }
public bool CodeAllowCommits { get; set; }
public SourceDto? Source { get; set; }
public DiscordWebHookDto? DiscordWebHook { get; set; }
}

View File

@ -0,0 +1,10 @@
namespace jtom38.Newsbot.Domain.Models.Collector;
public class SubscriptionDto
{
public Guid Id { get; set; }
public Guid SourceId { get; set; }
public Guid DiscordWebHookId { get; set; }
public bool CodeAllowReleases { get; set; }
public bool CodeAllowCommits { get; set; }
}

View File

@ -0,0 +1,7 @@
namespace jtom38.Newsbot.Domain.Models;
public class ConfigModel
{
public string? Uri { get; set; }
public string? ApiToken { get; set; }
}

View File

@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<RootNamespace>jtom38.Newsbot.Domain</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,27 @@
using Newtonsoft.Json;
using jtom38.Newsbot.Domain.Models.Collector;
namespace Newsbot.Services.Collector;
public class ArticlesClient
{
private readonly HttpClient _client;
private string? InstanceUri { get; set; }
public ArticlesClient(string? instanceUri)
{
_client = new HttpClient();
InstanceUri = instanceUri;
}
public async Task<List<ArticlesDto>> ListArticles()
{
var resp = await _client.GetAsync($"{InstanceUri}/api/articles");
var content = await resp.Content.ReadAsStringAsync();
var res = JsonConvert.DeserializeObject<List<ArticlesDto>>(content);
res ??= new List<ArticlesDto>();
return res;
}
}

View File

@ -0,0 +1,25 @@
using jtom38.Newsbot.Domain.Models;
namespace jtom38.Newsbot.Services.Config;
public class SetInstanceService
{
public static async Task Save(string uri)
{
var containsHttp = uri.Contains("http://");
var containsHttps = uri.Contains("https://");
if (!containsHttp && !containsHttps)
{
throw new Exception("Uri does not define http or https.");
}
Console.WriteLine($"Instance Set to '{uri}'");
var cfg = await ConfigClient.LoadAsync();
cfg ??= new ConfigModel();
cfg.Uri = uri ?? "";
await ConfigClient.SaveAsync(cfg);
}
}

View File

@ -0,0 +1,55 @@
using System.Text.Json;
using jtom38.Newsbot.Domain.Models;
namespace jtom38.Newsbot.Services;
public static class ConfigClient
{
public static async Task<ConfigModel> LoadAsync()
{
var blank = new ConfigModel();
try
{
var cfg = await LoadFileAsync();
return cfg;
}
catch (DirectoryNotFoundException)
{
Directory.CreateDirectory(GetConfigPath());
await SaveAsync(blank);
return blank;
}
catch (FileNotFoundException)
{
await SaveAsync(blank);
return blank;
}
}
public static async Task SaveAsync(ConfigModel config)
{
var jsonString = JsonSerializer.Serialize(config, new JsonSerializerOptions { WriteIndented = true});
await File.WriteAllTextAsync(GetConfigPath(), jsonString);
}
private static string GetConfigDirectory()
{
var appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
var path = Path.Join(appDataPath, "newsbot-cli");
return path;
}
private static string GetConfigPath()
{
var path = Path.Join(GetConfigDirectory(), "config.json");
return path;
}
private static async Task<ConfigModel> LoadFileAsync()
{
var content = await File.ReadAllTextAsync(GetConfigPath());
var res = JsonSerializer.Deserialize<ConfigModel>(content);
return res ?? new ConfigModel();
}
}

View File

@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<RootNamespace>jtom38.Newsbot.Services</RootNamespace>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Newsbot.Domain\Newsbot.Domain.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,18 @@
namespace jtom38.Newsbot.Services.Terminal;
public class UserInputService
{
public UserInputService()
{
}
public static async Task<string> WaitForInput(string message)
{
await Task.Delay(1);
Console.WriteLine(message);
Console.WriteLine("Enter your selection or press 'q' to exit");
var input = Console.ReadLine();
return input ?? "";
}
}

View File

@ -0,0 +1,20 @@
using jtom38.Newsbot.Domain.Models.Collector;
using Newsbot.Services.Collector;
namespace Newsbot.Tests.Clients;
public class ArticlesClientTests
{
[Fact]
public async Task ArticlesAreReturned()
{
var c = new ArticlesClient("http://192.168.1.225:5001");
var res = await c.ListArticles();
res ??= new List<ArticlesDto>();
if (res.Count == 0)
{
Assert.Fail("No results came back");
}
}
}

View File

@ -0,0 +1,6 @@
namespace Newsbot.Services.Config;
public class SetAuthKeyTest
{
}

View File

@ -0,0 +1,30 @@
using jtom38.Newsbot.Domain.Models;
using jtom38.Newsbot.Services;
using jtom38.Newsbot.Services.Config;
using Xunit.Abstractions;
namespace Newsbot.Tests.Commands;
public class SetInstanceTest
{
private readonly ITestOutputHelper _testOutputHelper;
public SetInstanceTest(ITestOutputHelper testOutputHelper)
{
_testOutputHelper = testOutputHelper;
}
[Fact]
public async Task SetInstanceSavesTheUri()
{
var uri = "http://localhost:5001";
try
{
await SetInstanceService.Save(uri);
}
catch(Exception ex)
{
Assert.Fail(ex.Message);
}
}
}

View File

@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
<PackageReference Include="coverlet.collector" Version="3.1.2" />
<PackageReference Include="xunit" Version="2.4.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Newsbot.Services\Newsbot.Services.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,18 @@
using jtom38.Newsbot.Domain.Models;
using jtom38.Newsbot.Services;
namespace Newsbot.Tests.Services;
public class ConfigClientTests
{
[Fact]
public async Task CanLoadConfig()
{
var cfg = await ConfigClient.LoadAsync();
cfg = cfg ??= new ConfigModel();
if (cfg.Uri == "")
{
Assert.Fail("Uri was missing");
}
}
}

1
Newsbot.Tests/Usings.cs Normal file
View File

@ -0,0 +1 @@
global using Xunit;

View File

@ -1,2 +0,0 @@
# Newsbot.Cli

12
makefile Normal file
View File

@ -0,0 +1,12 @@
.PHONY: help
help: ## Shows this help command
@egrep -h '\s##\s' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}'
build: ## builds the application with the current go runtime
dotnet restore
dotnet build
pub: ## Generate artifacts
dotnet restore
dotnet publish
cp ./Newsbot.Cli/bin/Debug/net7.0/osx-arm64/publish/Newsbot.Cli ./newsbot