Add Github Plugin

This commit is contained in:
Connor Drahoss 2022-09-06 22:24:23 -07:00
parent 840379bfda
commit dc116a33ab
Signed by: CanadianBacon
GPG Key ID: 410B7F84E17E4424
11 changed files with 388 additions and 23 deletions

View File

@ -5,11 +5,11 @@ namespace ExtensiblePortfolioSite.SDK.Git
{
internal static class GitManager
{
private static readonly SortedDictionary<String, GitService> Providers = new ();
private static readonly SortedDictionary<String, GitService> Providers = new();
public static void Register(String Service, IGitProvider Provider)
{
if(Providers.TryGetValue(Service, out GitService? GitService))
if (Providers.TryGetValue(Service, out GitService? GitService))
{
GitService.addProvider(Provider);
return;

View File

@ -1,8 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ExtensiblePortfolioSite.SDK.Git
{
@ -30,6 +26,11 @@ namespace ExtensiblePortfolioSite.SDK.Git
/// </summary>
public readonly struct GitReference
{
public GitReference(GitReferenceKind Kind, String ReferenceString)
{
this.Kind = Kind;
this.ReferenceString = ReferenceString;
}
/// <summary>
/// Reference Kind
/// </summary>

View File

@ -1,10 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ExtensiblePortfolioSite.SDK.Git
namespace ExtensiblePortfolioSite.SDK.Git
{
/// <summary>
/// Represents a Git Object Interface

View File

@ -1,9 +1,4 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ExtensiblePortfolioSite.SDK.Git
{
@ -26,6 +21,29 @@ namespace ExtensiblePortfolioSite.SDK.Git
/// </summary>
/// <param name="HeadOffset">How far to go back in the commit tree (default 0)</param>
/// <returns>A commit</returns>
public ICommit GetCommit(uint HeadOffset = 0);
public ICommit? GetCommit(uint HeadOffset = 0);
/// <summary>
/// Get a commit from the repository, defaults to latest
/// </summary>
/// <param name="HeadOffset">How far to go back in the commit tree (default 0)</param>
/// <param name="Commit">Reference to the commit</param>
/// <returns>If the commit could be found</returns>
public bool TryGetCommit(uint HeadOffset, out ICommit? Commit);
/// <summary>
/// Get a commit from the repository by it's hash
/// </summary>
/// <param name="Reference">Which commit to get from the repo</param>
/// <returns>A commit</returns>
public ICommit? GetCommitByRef(string Reference);
/// <summary>
/// Get a commit from the repository, defaults to latest
/// </summary>
/// <param name="Reference">Which commit to get from the repo</param>
/// <param name="Commit">Reference to the commit</param>
/// <returns>If the commit could be found</returns>
public bool TryGetCommitByRef(string Reference, out ICommit? Commit);
}
}

View File

@ -28,6 +28,7 @@ namespace ExtensiblePortfolioSite.SDK.Plugins
List<Byte[]> PublicKeyTokens = new();
PublicKeyTokens.Add(typeof(String).Assembly.GetName().GetPublicKeyToken()!); // Framework Libraries
PublicKeyTokens.Add(typeof(System.Runtime.GCSettings).Assembly.GetName().GetPublicKeyToken()!);
KnownPublicKeyTokens = PublicKeyTokens.ToArray();
}
@ -42,11 +43,13 @@ namespace ExtensiblePortfolioSite.SDK.Plugins
public static void LoadPlugins(string RootPath)
{
RootPath = Path.GetFullPath(RootPath);
if(!Directory.Exists(RootPath))
if (!Directory.Exists(RootPath))
Directory.CreateDirectory(RootPath);
LibraryPaths.Add(RootPath);
foreach (String Dll in Directory.EnumerateFiles(RootPath, "*.dll", SearchOption.TopDirectoryOnly))
{
TryLoadPlugin(Path.GetFullPath(Dll, RootPath), out Plugin? _);
}
}
public static Boolean TryLoadPlugin(string AsmPath, out Plugin? plugin)
{
@ -104,17 +107,17 @@ namespace ExtensiblePortfolioSite.SDK.Plugins
TypeReference AttrType = MD.GetTypeReference((TypeReferenceHandle)Ctor.Parent);
String AttrTypeName = MD.GetString(AttrType.Name);
String AttrTypeNamespace = MD.GetString(AttrType.Namespace);
if (
AttrType.ResolutionScope.Kind == HandleKind.AssemblyReference &&
AttrTypeName == EPSPluginAttribute_type.Name &&
AttrTypeNamespace == EPSPluginAttribute_type.Namespace
)
{
AssemblyReference AsmRef = MD.GetAssemblyReference((AssemblyReferenceHandle)AttrType.ResolutionScope);
String AsmName = MD.GetString(AsmRef.Name);
if (AsmName == EPSPluginAttribute_type.Assembly.FullName)
if (AsmName == EPSPluginAttribute_type.Assembly.GetName().Name)
return true;
}
}
@ -185,7 +188,7 @@ namespace ExtensiblePortfolioSite.SDK.Plugins
}
}
}
throw new FileNotFoundException($"Unable to load Assembly '{AsmName}'");
return null;
}
internal static IPluginUnmanagedLibrary? LoadUnmanagedLibrary(String Name)
{

View File

@ -0,0 +1,33 @@
using ExtensiblePortfolioSite.SDK.Git;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
namespace GithubPlugin
{
public class GithubCommit : ICommit
{
[JsonIgnore]
public IReadOnlyCollection<string> ModifiedFiles { get; internal set; }
[JsonIgnore]
public IRepository? Repository { get; internal set; }
[JsonIgnore]
public IGitProvider? Provider { get; internal set; }
[JsonPropertyName("sha")]
public string? Hash { get; private set; }
[JsonPropertyName("message")]
public string? Description { get; private set; }
public GitReference GetReference()
{
return new GitReference(GitReferenceKind.Commit, $"{Repository.Owner}/{Repository.Name}/{Hash}");
}
}
}

View File

@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\EPS.SDK\EPS.SDK.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,147 @@
using ExtensiblePortfolioSite.SDK.Git;
using System.Text.Json;
using System.Text.RegularExpressions;
namespace GithubPlugin
{
[GitProvider("github.com")]
public class GithubProvider : IGitProvider
{
private HttpClient httpClient = new HttpClient();
private Regex userPattern = new Regex("^(http(s)*:\\/\\/)(www\\.)*(github\\.com\\/[A-z]+)$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
private Regex repoPattern = new Regex("^(http(s)*:\\/\\/)(www\\.)*(github\\.com\\/[A-z]+\\/[A-z]+)$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
public Dictionary<string, GithubUser> Users = new();
public Dictionary<string, GithubRepo> Repositories = new();
public GithubProvider()
{
this.httpClient.BaseAddress = new("https://api.github.com/");
this.httpClient.DefaultRequestHeaders.Add("User-Agent", "KoromaruKoruko");
this.httpClient.DefaultRequestHeaders.Add("Authorization", "Bearer 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");
}
public IGitObject GetByReference(GitReference Reference)
{
switch(Reference.Kind)
{
case GitReferenceKind.User:
if(TryGetUserByName(Reference.ReferenceString, out IUser? user))
return user!;
else
return null;
case GitReferenceKind.Repository:
{
if (TryGetRepositoryByName(
Reference.ReferenceString.Split('/')[0], Reference.ReferenceString.Split('/')[1], out IRepository? repo))
return repo!;
else
return null;
}
case GitReferenceKind.Commit:
{
if (TryGetRepositoryByName(
Reference.ReferenceString.Split('/')[0], Reference.ReferenceString.Split('/')[1], out IRepository? repo))
{
if (repo!.TryGetCommitByRef(Reference.ReferenceString.Split('/')[2], out ICommit? commit))
return commit!;
else
return null;
}
else
return null;
}
}
return null;
}
public bool TryGetRepositoryByName(string Username, string RepositoryName, out IRepository? Repository)
{
if (TryGetUserByName(Username, out IUser? User))
return TryGetRepositoryByName(User!, RepositoryName, out Repository);
Repository = null;
return false;
}
public bool TryGetRepositoryByName(IUser User, string RepositoryName, out IRepository? Repository)
{
if(User.GetType() == typeof(GithubUser))
{
if (Repositories.TryGetValue(RepositoryName, out GithubRepo? _repo))
{
Repository = _repo;
return true;
} else
{
var response = httpClient.GetAsync($"repos/{User.Name}/{RepositoryName}").Result;
if (!response.IsSuccessStatusCode)
{
Repository = null;
return false;
}
GithubRepo tempRepo = JsonSerializer.Deserialize<GithubRepo>(response.Content.ReadAsStream())!;
tempRepo.Provider = this;
tempRepo.Owner = User;
Repository = tempRepo;
return true;
}
}
Repository = null;
return false;
}
public bool TryGetUserByName(string Username, out IUser? User)
{
if(Users.TryGetValue(Username, out GithubUser? _user))
{
User = _user;
return true;
} else
{
var response = httpClient.GetAsync($"users/{Username}").Result;
if(!response.IsSuccessStatusCode)
{
User = null;
return false;
}
GithubUser tempUser = JsonSerializer.Deserialize<GithubUser>(response.Content.ReadAsStream())!;
tempUser.Provider = this;
User = tempUser;
return true;
}
}
public bool TryGetRepositoryByURL(Uri RepoUrl, out IRepository? Repository)
{
if (repoPattern.IsMatch(RepoUrl.OriginalString))
{
var split = RepoUrl.OriginalString.Split("://")[1].Split('/');
return TryGetRepositoryByName(split[1], split[2], out Repository);
} else
{
Repository = null;
return false;
}
}
public bool TryGetUserByURL(Uri UserProfile, out IUser? User)
{
if (userPattern.IsMatch(UserProfile.OriginalString))
{
var split = UserProfile.OriginalString.Split("://")[1].Split('/');
return TryGetUserByName(split[1], out User);
}
else
{
User = null;
return false;
}
}
public HttpResponseMessage GetAPIResource(string resourceLocation)
{
return httpClient.GetAsync(resourceLocation).Result;
}
}
}

106
GithubPlugin/GithubRepo.cs Normal file
View File

@ -0,0 +1,106 @@
using ExtensiblePortfolioSite.SDK.Git;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
namespace GithubPlugin
{
public class GithubRepo : IRepository
{
[JsonPropertyName("name")]
public string Name { get; private set; }
[JsonPropertyName("description")]
public string Description { get; private set; }
[JsonIgnore]
public IUser Owner { get; internal set; }
[JsonIgnore]
public IGitProvider Provider { get; internal set; }
public ICommit? GetCommit(uint HeadOffset = 0)
{
var response = Provider.GetAPIResource($"repos/{Owner.Name}/{Name}/commits");
if(response.IsSuccessStatusCode)
{
JsonDocument json = JsonDocument.Parse(response.Content.ReadAsStream());
JsonElement commitObject = json.RootElement.EnumerateArray().Skip((int)HeadOffset).First();
GithubCommit commit = JsonSerializer.Deserialize<GithubCommit>(commitObject.GetProperty("commit"))!;
commit.Provider = Provider;
commit.Repository = this;
List<string> tempFiles = new List<string>();
JsonElement files = commitObject.GetProperty("files");
foreach(JsonElement file in files.EnumerateArray())
{
tempFiles.Add(file.GetProperty("filename").GetString()!);
}
commit.ModifiedFiles = tempFiles;
return commit;
} else
{
return null;
}
}
public ICommit? GetCommitByRef(string reference)
{
var response = Provider.GetAPIResource($"repos/{Owner.Name}/{Name}/commits/{reference}");
if (response.IsSuccessStatusCode)
{
JsonDocument json = JsonDocument.Parse(response.Content.ReadAsStream());
JsonElement commitObject = json.RootElement;
GithubCommit commit = JsonSerializer.Deserialize<GithubCommit>(commitObject.GetProperty("commit"))!;
commit.Provider = Provider;
commit.Repository = this;
List<string> tempFiles = new List<string>();
JsonElement files = commitObject.GetProperty("files");
foreach (JsonElement file in files.EnumerateArray())
{
tempFiles.Add(file.GetProperty("filename").GetString()!);
}
commit.ModifiedFiles = tempFiles;
return commit;
}
else
{
return null;
}
}
public GitReference GetReference()
{
return new GitReference(GitReferenceKind.Repository, $"{Owner}/{Name}");
}
public bool TryGetCommit(uint HeadOffset, out ICommit? Commit)
{
var commit = GetCommit(HeadOffset);
if (commit != null)
{
Commit = commit;
return true;
}
Commit = null;
return false;
}
public bool TryGetCommitByRef(string Reference, out ICommit? Commit)
{
var commit = GetCommitByRef(Reference);
if (commit != null)
{
Commit = commit;
return true;
}
Commit = null;
return false;
}
}
}

View File

@ -0,0 +1,47 @@
using ExtensiblePortfolioSite.SDK.Git;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
namespace GithubPlugin
{
public class GithubUser : IUser
{
[JsonPropertyName("login")]
public string Name { get; private set; }
[JsonPropertyName("id")]
public int Identifier { get; private set; }
[JsonPropertyName("avatar_url")]
public string AvatarURL { get; private set; }
[JsonIgnore]
public IGitProvider Provider { get; internal set; }
public GitReference GetReference()
{
return new GitReference(GitReferenceKind.User, $"{Name}");
}
public IEnumerable<IRepository> GetUserRepositories()
{
var response = Provider.GetAPIResource($"users/{Name}/repos");
if(response.IsSuccessStatusCode)
{
JsonDocument json = JsonDocument.Parse(response.Content.ReadAsStream());
List<IRepository> ret = new List<IRepository>(json.RootElement.GetArrayLength());
foreach(JsonElement repo in json.RootElement.EnumerateArray())
{
ret.Add(JsonSerializer.Deserialize<GithubRepo>(repo)!);
}
return ret;
}
return new List<IRepository>();
}
}
}

View File

@ -0,0 +1,3 @@
using ExtensiblePortfolioSite.SDK.Plugins;
[assembly: EPSPlugin("Github", false, 1, 0)]