From 0f8b29747727a0f3b11a536012ae977cf58d9533 Mon Sep 17 00:00:00 2001 From: Hootan HT Date: Mon, 27 Mar 2023 00:17:52 +0330 Subject: [PATCH 01/53] Get badge file from github api. --- .../ServerGitHubBadgeService.cs | 41 +++++++++++++++---- 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/src/CrystallineSociety/Server/Api/Services/Implementations/ServerGitHubBadgeService.cs b/src/CrystallineSociety/Server/Api/Services/Implementations/ServerGitHubBadgeService.cs index 55be8194..d6a7df6c 100644 --- a/src/CrystallineSociety/Server/Api/Services/Implementations/ServerGitHubBadgeService.cs +++ b/src/CrystallineSociety/Server/Api/Services/Implementations/ServerGitHubBadgeService.cs @@ -20,20 +20,43 @@ public async Task> GetBadgesAsync(string url) public async Task GetBadgeAsync(string url) { - throw new NotImplementedException(); - //var client = new GitHubClient(new ProductHeaderValue("CS-System")); - //var repos = await client.Repository.GetAllForOrg("cs-internship"); - //var repo = repos.First(r => r.Name == "cs-system"); + //throw new NotImplementedException(); + var client = new GitHubClient(new ProductHeaderValue("CS-System")); + var repos = await client.Repository.GetAllForOrg("cs-internship"); + var repo = repos.First(r => r.Name == "cs-system"); - //var refs= await client.Git.Reference.GetAll(repo.Id); + var refs = await client.Git.Reference.GetAll(repo.Id); //var main = refs.First(r => r.Ref.Contains("refs/heads/main")); - ////var main = refs.First(r => r.Ref.Contains("refs/heads/main")); - //var refx = - // "refs/heads/main/src/CrystallineSociety/Shared/CrystallineSociety.Shared.Test/BadgeSystem/SampleBadgeDocs/serialization-badge-sample"; + var main = refs.First(r => r.Ref.Contains("refs/heads/main")); + //var getCorrectUrl = url.IndexOf("/main", StringComparison.Ordinal); + + //if (getCorrectUrl < 0 || getCorrectUrl >= url.Length) + // return null; + + //var refUrl = + // $"refs/heads{url.Substring(getCorrectUrl)}"; + //var xx = await client.Git.Tree.GetRecursive(repo.Id, main.Ref); + ////https://github.com/cs-internship/cs-system/tree/main/src/CrystallineSociety/Shared/CrystallineSociety.Shared.Test/BadgeSystem/SampleBadgeDocs/serialization-badge-sample + //List sample = new List(); + //foreach (var item in xx.Tree) + //{ + // sample.Add(item); + //} + var getCorrectUrlForSrc = url.IndexOf("src", StringComparison.Ordinal); + var folderName = url.Substring(getCorrectUrlForSrc); + var contents = await client.Repository.Content.GetAllContents(repo.Id, folderName); + var badgeFilePath = contents.FirstOrDefault(x => x.Name.Contains(".json"))?.Path; + var badgeFile = await client.Repository.Content.GetAllContents(repo.Id, badgeFilePath); + var badgeFileContent = badgeFile.FirstOrDefault()?.Content; + var badge = new BadgeDto(); + if (badgeFileContent != null) + { + badge = BadgeUtilService.ParseBadge(badgeFileContent); + } - //return default; + return badge; } } } From 185d6206d3a9fde441baddfbcf1632e5b38cc149 Mon Sep 17 00:00:00 2001 From: Hootan HT Date: Tue, 28 Mar 2023 13:50:02 +0330 Subject: [PATCH 02/53] Re factor GetBadgeAsync method. --- .../ServerGitHubBadgeService.cs | 41 +++++++------------ 1 file changed, 14 insertions(+), 27 deletions(-) diff --git a/src/CrystallineSociety/Server/Api/Services/Implementations/ServerGitHubBadgeService.cs b/src/CrystallineSociety/Server/Api/Services/Implementations/ServerGitHubBadgeService.cs index d6a7df6c..d4ef7700 100644 --- a/src/CrystallineSociety/Server/Api/Services/Implementations/ServerGitHubBadgeService.cs +++ b/src/CrystallineSociety/Server/Api/Services/Implementations/ServerGitHubBadgeService.cs @@ -20,41 +20,28 @@ public async Task> GetBadgesAsync(string url) public async Task GetBadgeAsync(string url) { - //throw new NotImplementedException(); var client = new GitHubClient(new ProductHeaderValue("CS-System")); var repos = await client.Repository.GetAllForOrg("cs-internship"); var repo = repos.First(r => r.Name == "cs-system"); - - var refs = await client.Git.Reference.GetAll(repo.Id); - - //var main = refs.First(r => r.Ref.Contains("refs/heads/main")); - var main = refs.First(r => r.Ref.Contains("refs/heads/main")); - //var getCorrectUrl = url.IndexOf("/main", StringComparison.Ordinal); - - //if (getCorrectUrl < 0 || getCorrectUrl >= url.Length) - // return null; - - //var refUrl = - // $"refs/heads{url.Substring(getCorrectUrl)}"; - - //var xx = await client.Git.Tree.GetRecursive(repo.Id, main.Ref); - ////https://github.com/cs-internship/cs-system/tree/main/src/CrystallineSociety/Shared/CrystallineSociety.Shared.Test/BadgeSystem/SampleBadgeDocs/serialization-badge-sample - //List sample = new List(); - //foreach (var item in xx.Tree) - //{ - // sample.Add(item); - //} - var getCorrectUrlForSrc = url.IndexOf("src", StringComparison.Ordinal); - var folderName = url.Substring(getCorrectUrlForSrc); - var contents = await client.Repository.Content.GetAllContents(repo.Id, folderName); - var badgeFilePath = contents.FirstOrDefault(x => x.Name.Contains(".json"))?.Path; + var urlSrcIndex = url.IndexOf("src", StringComparison.Ordinal); + var folderPath = url[urlSrcIndex..]; + var folderContents = await client.Repository.Content.GetAllContents(repo.Id, folderPath); + var badgeFilePath = folderContents.FirstOrDefault(x => x.Name.Contains(".json"))?.Path; var badgeFile = await client.Repository.Content.GetAllContents(repo.Id, badgeFilePath); - var badgeFileContent = badgeFile.FirstOrDefault()?.Content; var badge = new BadgeDto(); - if (badgeFileContent != null) + var badgeFileContent = badgeFile?.FirstOrDefault()?.Content; + + if (badgeFileContent == null) + return badge; + + try { badge = BadgeUtilService.ParseBadge(badgeFileContent); } + catch (Exception e) + { + Console.WriteLine(e); + } return badge; } From b7c1306fde4d3401089f49b622ed2b80f29c5973 Mon Sep 17 00:00:00 2001 From: Hootan HT Date: Wed, 29 Mar 2023 00:24:57 +0330 Subject: [PATCH 03/53] Implement GetBadgesAsync method. --- .../ServerGitHubBadgeService.cs | 59 ++++++++++++++++--- 1 file changed, 50 insertions(+), 9 deletions(-) diff --git a/src/CrystallineSociety/Server/Api/Services/Implementations/ServerGitHubBadgeService.cs b/src/CrystallineSociety/Server/Api/Services/Implementations/ServerGitHubBadgeService.cs index d4ef7700..960937a4 100644 --- a/src/CrystallineSociety/Server/Api/Services/Implementations/ServerGitHubBadgeService.cs +++ b/src/CrystallineSociety/Server/Api/Services/Implementations/ServerGitHubBadgeService.cs @@ -1,4 +1,8 @@ -using CrystallineSociety.Shared.Dtos.BadgeSystem; +using System.Reflection.Metadata; +using System.Text; + +using CrystallineSociety.Shared.Dtos.BadgeSystem; + using Octokit; namespace CrystallineSociety.Server.Api.Services.Implementations @@ -10,12 +14,31 @@ public partial class ServerGitHubBadgeService : IGitHubBadgeService public async Task> GetBadgesAsync(string url) { - // Todo: return the real list. - return new List() + var client = new GitHubClient(new ProductHeaderValue("CS-System")); + var repos = await client.Repository.GetAllForOrg("cs-internship"); + var repo = repos.First(r => r.Name == "cs-system"); + var lastSegment = GetLastSegmentFromUrl(url, out var parentFolderPath); + var folderContents = await client.Repository.Content.GetAllContents(repo.Id, parentFolderPath); + var destinationFolderSha = folderContents?.First(f => f.Name == lastSegment).Sha; + var destinationFolderContents = await client.Git.Tree.GetRecursive(repo.Id, destinationFolderSha); + + var badges = new List(); + + foreach (var item in destinationFolderContents.Tree) { - BadgeUtilService.ParseBadge($$"""{"code": "github-test-badge-a", "description": "from: {{url}}"}"""), - BadgeUtilService.ParseBadge($$"""{"code": "github-test-badge-b", "description": "from: {{url}}"}"""), - }; + if (Path.GetExtension(item.Path) != ".json") + continue; + + var getBadge = await client.Git.Blob.Get(repo.Id,item.Sha); + + if (getBadge.Encoding != EncodingType.Base64) + continue; + + var bytes = Convert.FromBase64String(getBadge.Content); + var badgeContent = Encoding.UTF8.GetString(bytes); + badges.Add(BadgeUtilService.ParseBadge(badgeContent)); + } + return badges; } public async Task GetBadgeAsync(string url) @@ -23,10 +46,9 @@ public async Task GetBadgeAsync(string url) var client = new GitHubClient(new ProductHeaderValue("CS-System")); var repos = await client.Repository.GetAllForOrg("cs-internship"); var repo = repos.First(r => r.Name == "cs-system"); - var urlSrcIndex = url.IndexOf("src", StringComparison.Ordinal); - var folderPath = url[urlSrcIndex..]; + var folderPath = GetRelativeFolderPath(url); var folderContents = await client.Repository.Content.GetAllContents(repo.Id, folderPath); - var badgeFilePath = folderContents.FirstOrDefault(x => x.Name.Contains(".json"))?.Path; + var badgeFilePath = folderContents.FirstOrDefault(x => Path.GetExtension(x.Name) == ".json")?.Path; var badgeFile = await client.Repository.Content.GetAllContents(repo.Id, badgeFilePath); var badge = new BadgeDto(); var badgeFileContent = badgeFile?.FirstOrDefault()?.Content; @@ -45,5 +67,24 @@ public async Task GetBadgeAsync(string url) return badge; } + + private static string GetRelativeFolderPath(string url) + { + var urlSrcIndex = url.IndexOf("src", StringComparison.Ordinal); + var folderPath = url[urlSrcIndex..]; + return folderPath; + } + + private static string GetLastSegmentFromUrl(string url, out string parentFolderPath) + { + var uri = new Uri(url); + var lastSegment = uri.Segments.Last().TrimEnd('/'); + var parentFolderUrl = uri.GetLeftPart(UriPartial.Authority) + + string.Join("", uri.Segments.Take(uri.Segments.Length - 1)); + var urlSrcIndex = parentFolderUrl.IndexOf("src", StringComparison.Ordinal); + parentFolderPath = parentFolderUrl[urlSrcIndex..]; + + return lastSegment; + } } } From 8b70fd7c9fc8dccf64a1a27cb9dfd4f3b01a0a08 Mon Sep 17 00:00:00 2001 From: Hootan HT Date: Wed, 29 Mar 2023 12:54:48 +0330 Subject: [PATCH 04/53] Re factor GetBadgesAsync. --- .../Services/Implementations/ServerGitHubBadgeService.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/CrystallineSociety/Server/Api/Services/Implementations/ServerGitHubBadgeService.cs b/src/CrystallineSociety/Server/Api/Services/Implementations/ServerGitHubBadgeService.cs index 960937a4..98b64784 100644 --- a/src/CrystallineSociety/Server/Api/Services/Implementations/ServerGitHubBadgeService.cs +++ b/src/CrystallineSociety/Server/Api/Services/Implementations/ServerGitHubBadgeService.cs @@ -29,12 +29,12 @@ public async Task> GetBadgesAsync(string url) if (Path.GetExtension(item.Path) != ".json") continue; - var getBadge = await client.Git.Blob.Get(repo.Id,item.Sha); + var badgeBlob = await client.Git.Blob.Get(repo.Id,item.Sha); - if (getBadge.Encoding != EncodingType.Base64) + if (badgeBlob.Encoding != EncodingType.Base64) continue; - var bytes = Convert.FromBase64String(getBadge.Content); + var bytes = Convert.FromBase64String(badgeBlob.Content); var badgeContent = Encoding.UTF8.GetString(bytes); badges.Add(BadgeUtilService.ParseBadge(badgeContent)); } From 4d679b14eeae1fd5e94ae30c6405eec24624719a Mon Sep 17 00:00:00 2001 From: Mehran Davoudi Date: Wed, 29 Mar 2023 14:07:10 +0330 Subject: [PATCH 05/53] Add some exception management to GitHubBadgeService --- .../ServerGitHubBadgeService.cs | 34 +++++++++++++------ .../Services/Contracts/IGitHubBadgeService.cs | 13 +++++++ 2 files changed, 37 insertions(+), 10 deletions(-) diff --git a/src/CrystallineSociety/Server/Api/Services/Implementations/ServerGitHubBadgeService.cs b/src/CrystallineSociety/Server/Api/Services/Implementations/ServerGitHubBadgeService.cs index 98b64784..752668ab 100644 --- a/src/CrystallineSociety/Server/Api/Services/Implementations/ServerGitHubBadgeService.cs +++ b/src/CrystallineSociety/Server/Api/Services/Implementations/ServerGitHubBadgeService.cs @@ -44,28 +44,42 @@ public async Task> GetBadgesAsync(string url) public async Task GetBadgeAsync(string url) { var client = new GitHubClient(new ProductHeaderValue("CS-System")); - var repos = await client.Repository.GetAllForOrg("cs-internship"); + + //var organization = + + // ToDo: get from url + var repos = + await client.Repository.GetAllForOrg("cs-internship") + ?? throw new ResourceNotFoundException($"Unable to locate organization: {url}"); + + var repo = repos.First(r => r.Name == "cs-system"); var folderPath = GetRelativeFolderPath(url); var folderContents = await client.Repository.Content.GetAllContents(repo.Id, folderPath); - var badgeFilePath = folderContents.FirstOrDefault(x => Path.GetExtension(x.Name) == ".json")?.Path; - var badgeFile = await client.Repository.Content.GetAllContents(repo.Id, badgeFilePath); - var badge = new BadgeDto(); - var badgeFileContent = badgeFile?.FirstOrDefault()?.Content; + var badgeFilePath = + folderContents.FirstOrDefault(x => Path.GetExtension(x.Name) == ".json")?.Path + ?? throw new ResourceNotFoundException($"Unable to locate badge url: {url}"); + + //if (badgeFilePath is null) + // throw new ResourceNotFoundException($"Unable to locate badge url: {url}"); + + var contents = await client.Repository.Content.GetAllContents(repo.Id, badgeFilePath); + var badgeFile = contents.FirstOrDefault(); + var badgeFileContent = badgeFile?.Content; if (badgeFileContent == null) - return badge; + return null; try { - badge = BadgeUtilService.ParseBadge(badgeFileContent); + var badge = BadgeUtilService.ParseBadge(badgeFileContent); + return badge; } - catch (Exception e) + catch (Exception exception) { - Console.WriteLine(e); + throw new FormatException($"Can not parse badge with url: '{url}' ", exception); } - return badge; } private static string GetRelativeFolderPath(string url) diff --git a/src/CrystallineSociety/Shared/Shared/Services/Contracts/IGitHubBadgeService.cs b/src/CrystallineSociety/Shared/Shared/Services/Contracts/IGitHubBadgeService.cs index 2f1a100b..faa791f2 100644 --- a/src/CrystallineSociety/Shared/Shared/Services/Contracts/IGitHubBadgeService.cs +++ b/src/CrystallineSociety/Shared/Shared/Services/Contracts/IGitHubBadgeService.cs @@ -9,7 +9,20 @@ namespace CrystallineSociety.Shared.Services.Contracts { public interface IGitHubBadgeService { + /// + /// + /// + /// + /// Task> GetBadgesAsync(string url); + + /// + /// Loads a badge spec from the given and parses and return a BadgeDto + /// + /// + /// Returns the parsed badge if url exists, otherwise return null + /// If the loaded url string is not in a correct format. + /// Task GetBadgeAsync(string url); } } From e1c306aedf52962bae3d401b77d0b1f1b1a4c9b7 Mon Sep 17 00:00:00 2001 From: Zahra Ahangari Date: Wed, 29 Mar 2023 20:25:10 +0330 Subject: [PATCH 06/53] Add documentation description for ResourceNotFoundException --- .../Shared/Shared/Services/Contracts/IGitHubBadgeService.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/CrystallineSociety/Shared/Shared/Services/Contracts/IGitHubBadgeService.cs b/src/CrystallineSociety/Shared/Shared/Services/Contracts/IGitHubBadgeService.cs index faa791f2..da455f92 100644 --- a/src/CrystallineSociety/Shared/Shared/Services/Contracts/IGitHubBadgeService.cs +++ b/src/CrystallineSociety/Shared/Shared/Services/Contracts/IGitHubBadgeService.cs @@ -17,12 +17,12 @@ public interface IGitHubBadgeService Task> GetBadgesAsync(string url); /// - /// Loads a badge spec from the given and parses and return a BadgeDto + /// Loads a badge spec from the given and parses it, and returns a BadgeDto. /// /// - /// Returns the parsed badge if url exists, otherwise return null + /// Returns the parsed badge. /// If the loaded url string is not in a correct format. - /// + /// If the given url could not be found. Task GetBadgeAsync(string url); } } From 1b78e236815959dfde659b835c0aa5115bbc8d1c Mon Sep 17 00:00:00 2001 From: Zahra Ahangari Date: Wed, 29 Mar 2023 20:31:46 +0330 Subject: [PATCH 07/53] Add FileContentIsNullException to AppStrings --- .../Shared/Shared/Resources/AppStrings.Designer.cs | 9 +++++++++ .../Shared/Shared/Resources/AppStrings.resx | 9 ++++----- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/CrystallineSociety/Shared/Shared/Resources/AppStrings.Designer.cs b/src/CrystallineSociety/Shared/Shared/Resources/AppStrings.Designer.cs index 040c053d..5c501970 100644 --- a/src/CrystallineSociety/Shared/Shared/Resources/AppStrings.Designer.cs +++ b/src/CrystallineSociety/Shared/Shared/Resources/AppStrings.Designer.cs @@ -502,6 +502,15 @@ public static string Error { } } + /// + /// Looks up a localized string similar to File content is null.. + /// + public static string FileContentIsNullException { + get { + return ResourceManager.GetString("FileContentIsNullException", resourceCulture); + } + } + /// /// Looks up a localized string similar to The {0} field only accepts files with the following extensions: {1}. /// diff --git a/src/CrystallineSociety/Shared/Shared/Resources/AppStrings.resx b/src/CrystallineSociety/Shared/Shared/Resources/AppStrings.resx index 086eca07..4a33cd58 100644 --- a/src/CrystallineSociety/Shared/Shared/Resources/AppStrings.resx +++ b/src/CrystallineSociety/Shared/Shared/Resources/AppStrings.resx @@ -207,8 +207,7 @@ Error when passwords do not have an uppercase letter - {0} must be at least {1} characters. - Error message for passwords that are too short + The field {0} must be a string or array type with a minimum length of '{1}'. Role {0} does not exist. @@ -535,9 +534,6 @@ Please confirm your email by clicking on the link. MinLengthAttribute must have a Length value that is zero or greater. - - The field {0} must be a string or array type with a minimum length of '{1}'. - The {0} field is not a valid phone number. @@ -649,4 +645,7 @@ Please confirm your email by clicking on the link. Title + + File content is null. + \ No newline at end of file From d8636aa29eb278a238f11b837e0617b787675ed9 Mon Sep 17 00:00:00 2001 From: Zahra Ahangari Date: Wed, 29 Mar 2023 20:32:36 +0330 Subject: [PATCH 08/53] Add FileContentIsNullException type --- .../ServerGitHubBadgeService.cs | 44 ++++++++++--------- .../Exceptions/FileContentIsNullException.cs | 21 +++++++++ 2 files changed, 45 insertions(+), 20 deletions(-) create mode 100644 src/CrystallineSociety/Shared/Shared/Exceptions/FileContentIsNullException.cs diff --git a/src/CrystallineSociety/Server/Api/Services/Implementations/ServerGitHubBadgeService.cs b/src/CrystallineSociety/Server/Api/Services/Implementations/ServerGitHubBadgeService.cs index 752668ab..6626e066 100644 --- a/src/CrystallineSociety/Server/Api/Services/Implementations/ServerGitHubBadgeService.cs +++ b/src/CrystallineSociety/Server/Api/Services/Implementations/ServerGitHubBadgeService.cs @@ -26,12 +26,12 @@ public async Task> GetBadgesAsync(string url) foreach (var item in destinationFolderContents.Tree) { - if (Path.GetExtension(item.Path) != ".json") + if (Path.GetExtension(item.Path) != ".json") continue; - var badgeBlob = await client.Git.Blob.Get(repo.Id,item.Sha); + var badgeBlob = await client.Git.Blob.Get(repo.Id, item.Sha); - if (badgeBlob.Encoding != EncodingType.Base64) + if (badgeBlob.Encoding != EncodingType.Base64) continue; var bytes = Convert.FromBase64String(badgeBlob.Content); @@ -44,31 +44,25 @@ public async Task> GetBadgesAsync(string url) public async Task GetBadgeAsync(string url) { var client = new GitHubClient(new ProductHeaderValue("CS-System")); + var (orgName, repoName) = GetRepoAndOrgNameFromUrl(url); - //var organization = + var repos = + await client.Repository.GetAllForOrg(orgName) + ?? throw new ResourceNotFoundException($"Unable to locate orgName: {url}"); - // ToDo: get from url - var repos = - await client.Repository.GetAllForOrg("cs-internship") - ?? throw new ResourceNotFoundException($"Unable to locate organization: {url}"); + var repo = + repos.First(r => r.Name == repoName) + ?? throw new ResourceNotFoundException($"Unable to locate repoName: {url}"); - - var repo = repos.First(r => r.Name == "cs-system"); var folderPath = GetRelativeFolderPath(url); var folderContents = await client.Repository.Content.GetAllContents(repo.Id, folderPath); - var badgeFilePath = - folderContents.FirstOrDefault(x => Path.GetExtension(x.Name) == ".json")?.Path + var badgeFilePath = + folderContents.FirstOrDefault(x => x.Name.EndsWith("-badge.json"))?.Path ?? throw new ResourceNotFoundException($"Unable to locate badge url: {url}"); - //if (badgeFilePath is null) - // throw new ResourceNotFoundException($"Unable to locate badge url: {url}"); - var contents = await client.Repository.Content.GetAllContents(repo.Id, badgeFilePath); - var badgeFile = contents.FirstOrDefault(); - var badgeFileContent = badgeFile?.Content; - - if (badgeFileContent == null) - return null; + var badgeFile = contents?.FirstOrDefault(); + var badgeFileContent = badgeFile?.Content ?? throw new FileContentIsNullException($"File content retrieved from {url} is null"); try { @@ -100,5 +94,15 @@ private static string GetLastSegmentFromUrl(string url, out string parentFolderP return lastSegment; } + + private static (string org, string repo) GetRepoAndOrgNameFromUrl(string url) + { + Uri uri = new(url); + string[] segments = uri.Segments; + string org = segments[1].TrimEnd('/'); + string repo = segments[2].TrimEnd('/'); + + return (org, repo); + } } } diff --git a/src/CrystallineSociety/Shared/Shared/Exceptions/FileContentIsNullException.cs b/src/CrystallineSociety/Shared/Shared/Exceptions/FileContentIsNullException.cs new file mode 100644 index 00000000..06c4d1e9 --- /dev/null +++ b/src/CrystallineSociety/Shared/Shared/Exceptions/FileContentIsNullException.cs @@ -0,0 +1,21 @@ +using System.Runtime.Serialization; + +namespace CrystallineSociety.Shared.Exceptions; + +public class FileContentIsNullException : IOException +{ + public FileContentIsNullException() + : base(nameof(AppStrings.FileContentIsNullException)) + { + } + + public FileContentIsNullException(string message) + : base(message) + { + } + + public FileContentIsNullException(string message, Exception? innerException) + : base(message, innerException) + { + } +} From 32f2a9a2e8956c56dcb4aaed7f05277eb22d9349 Mon Sep 17 00:00:00 2001 From: Zahra Ahangari Date: Wed, 29 Mar 2023 20:33:12 +0330 Subject: [PATCH 09/53] Revert "Add FileContentIsNullException type" This reverts commit d8636aa29eb278a238f11b837e0617b787675ed9. --- .../ServerGitHubBadgeService.cs | 44 +++++++++---------- .../Exceptions/FileContentIsNullException.cs | 21 --------- 2 files changed, 20 insertions(+), 45 deletions(-) delete mode 100644 src/CrystallineSociety/Shared/Shared/Exceptions/FileContentIsNullException.cs diff --git a/src/CrystallineSociety/Server/Api/Services/Implementations/ServerGitHubBadgeService.cs b/src/CrystallineSociety/Server/Api/Services/Implementations/ServerGitHubBadgeService.cs index 6626e066..752668ab 100644 --- a/src/CrystallineSociety/Server/Api/Services/Implementations/ServerGitHubBadgeService.cs +++ b/src/CrystallineSociety/Server/Api/Services/Implementations/ServerGitHubBadgeService.cs @@ -26,12 +26,12 @@ public async Task> GetBadgesAsync(string url) foreach (var item in destinationFolderContents.Tree) { - if (Path.GetExtension(item.Path) != ".json") + if (Path.GetExtension(item.Path) != ".json") continue; - var badgeBlob = await client.Git.Blob.Get(repo.Id, item.Sha); + var badgeBlob = await client.Git.Blob.Get(repo.Id,item.Sha); - if (badgeBlob.Encoding != EncodingType.Base64) + if (badgeBlob.Encoding != EncodingType.Base64) continue; var bytes = Convert.FromBase64String(badgeBlob.Content); @@ -44,25 +44,31 @@ public async Task> GetBadgesAsync(string url) public async Task GetBadgeAsync(string url) { var client = new GitHubClient(new ProductHeaderValue("CS-System")); - var (orgName, repoName) = GetRepoAndOrgNameFromUrl(url); - var repos = - await client.Repository.GetAllForOrg(orgName) - ?? throw new ResourceNotFoundException($"Unable to locate orgName: {url}"); + //var organization = - var repo = - repos.First(r => r.Name == repoName) - ?? throw new ResourceNotFoundException($"Unable to locate repoName: {url}"); + // ToDo: get from url + var repos = + await client.Repository.GetAllForOrg("cs-internship") + ?? throw new ResourceNotFoundException($"Unable to locate organization: {url}"); + + var repo = repos.First(r => r.Name == "cs-system"); var folderPath = GetRelativeFolderPath(url); var folderContents = await client.Repository.Content.GetAllContents(repo.Id, folderPath); - var badgeFilePath = - folderContents.FirstOrDefault(x => x.Name.EndsWith("-badge.json"))?.Path + var badgeFilePath = + folderContents.FirstOrDefault(x => Path.GetExtension(x.Name) == ".json")?.Path ?? throw new ResourceNotFoundException($"Unable to locate badge url: {url}"); + //if (badgeFilePath is null) + // throw new ResourceNotFoundException($"Unable to locate badge url: {url}"); + var contents = await client.Repository.Content.GetAllContents(repo.Id, badgeFilePath); - var badgeFile = contents?.FirstOrDefault(); - var badgeFileContent = badgeFile?.Content ?? throw new FileContentIsNullException($"File content retrieved from {url} is null"); + var badgeFile = contents.FirstOrDefault(); + var badgeFileContent = badgeFile?.Content; + + if (badgeFileContent == null) + return null; try { @@ -94,15 +100,5 @@ private static string GetLastSegmentFromUrl(string url, out string parentFolderP return lastSegment; } - - private static (string org, string repo) GetRepoAndOrgNameFromUrl(string url) - { - Uri uri = new(url); - string[] segments = uri.Segments; - string org = segments[1].TrimEnd('/'); - string repo = segments[2].TrimEnd('/'); - - return (org, repo); - } } } diff --git a/src/CrystallineSociety/Shared/Shared/Exceptions/FileContentIsNullException.cs b/src/CrystallineSociety/Shared/Shared/Exceptions/FileContentIsNullException.cs deleted file mode 100644 index 06c4d1e9..00000000 --- a/src/CrystallineSociety/Shared/Shared/Exceptions/FileContentIsNullException.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Runtime.Serialization; - -namespace CrystallineSociety.Shared.Exceptions; - -public class FileContentIsNullException : IOException -{ - public FileContentIsNullException() - : base(nameof(AppStrings.FileContentIsNullException)) - { - } - - public FileContentIsNullException(string message) - : base(message) - { - } - - public FileContentIsNullException(string message, Exception? innerException) - : base(message, innerException) - { - } -} From 0c667886847177169997d6c881acdb5f5c063a5e Mon Sep 17 00:00:00 2001 From: Zahra Ahangari Date: Wed, 29 Mar 2023 20:36:04 +0330 Subject: [PATCH 10/53] Add FileContentIsNullException type --- .../Exceptions/FileContentIsNullException.cs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 src/CrystallineSociety/Shared/Shared/Exceptions/FileContentIsNullException.cs diff --git a/src/CrystallineSociety/Shared/Shared/Exceptions/FileContentIsNullException.cs b/src/CrystallineSociety/Shared/Shared/Exceptions/FileContentIsNullException.cs new file mode 100644 index 00000000..06c4d1e9 --- /dev/null +++ b/src/CrystallineSociety/Shared/Shared/Exceptions/FileContentIsNullException.cs @@ -0,0 +1,21 @@ +using System.Runtime.Serialization; + +namespace CrystallineSociety.Shared.Exceptions; + +public class FileContentIsNullException : IOException +{ + public FileContentIsNullException() + : base(nameof(AppStrings.FileContentIsNullException)) + { + } + + public FileContentIsNullException(string message) + : base(message) + { + } + + public FileContentIsNullException(string message, Exception? innerException) + : base(message, innerException) + { + } +} From 99047d828df4fac6eb06da8c009322e7df991f32 Mon Sep 17 00:00:00 2001 From: Zahra Ahangari Date: Wed, 29 Mar 2023 20:41:27 +0330 Subject: [PATCH 11/53] Refactor GetBadgesAsync --- .../ServerGitHubBadgeService.cs | 45 ++++++++++--------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/src/CrystallineSociety/Server/Api/Services/Implementations/ServerGitHubBadgeService.cs b/src/CrystallineSociety/Server/Api/Services/Implementations/ServerGitHubBadgeService.cs index 752668ab..2f7fb687 100644 --- a/src/CrystallineSociety/Server/Api/Services/Implementations/ServerGitHubBadgeService.cs +++ b/src/CrystallineSociety/Server/Api/Services/Implementations/ServerGitHubBadgeService.cs @@ -26,12 +26,12 @@ public async Task> GetBadgesAsync(string url) foreach (var item in destinationFolderContents.Tree) { - if (Path.GetExtension(item.Path) != ".json") + if (Path.GetExtension(item.Path) != ".json") continue; - var badgeBlob = await client.Git.Blob.Get(repo.Id,item.Sha); + var badgeBlob = await client.Git.Blob.Get(repo.Id, item.Sha); - if (badgeBlob.Encoding != EncodingType.Base64) + if (badgeBlob.Encoding != EncodingType.Base64) continue; var bytes = Convert.FromBase64String(badgeBlob.Content); @@ -44,31 +44,26 @@ public async Task> GetBadgesAsync(string url) public async Task GetBadgeAsync(string url) { var client = new GitHubClient(new ProductHeaderValue("CS-System")); + var (orgName, repoName) = GetRepoAndOrgNameFromUrl(url); - //var organization = + var repos = + await client.Repository.GetAllForOrg(orgName) + ?? throw new ResourceNotFoundException($"Unable to locate orgName: {url}"); - // ToDo: get from url - var repos = - await client.Repository.GetAllForOrg("cs-internship") - ?? throw new ResourceNotFoundException($"Unable to locate organization: {url}"); + var repo = + repos.First(r => r.Name == repoName) + ?? throw new ResourceNotFoundException($"Unable to locate repoName: {url}"); - - var repo = repos.First(r => r.Name == "cs-system"); var folderPath = GetRelativeFolderPath(url); var folderContents = await client.Repository.Content.GetAllContents(repo.Id, folderPath); - var badgeFilePath = - folderContents.FirstOrDefault(x => Path.GetExtension(x.Name) == ".json")?.Path + var badgeFilePath = + folderContents.FirstOrDefault(x => x.Name.EndsWith("-badge.json"))?.Path ?? throw new ResourceNotFoundException($"Unable to locate badge url: {url}"); - //if (badgeFilePath is null) - // throw new ResourceNotFoundException($"Unable to locate badge url: {url}"); - var contents = await client.Repository.Content.GetAllContents(repo.Id, badgeFilePath); - var badgeFile = contents.FirstOrDefault(); - var badgeFileContent = badgeFile?.Content; - - if (badgeFileContent == null) - return null; + // ToDo: Dig a little deeper in each of these variables for being null + var badgeFile = contents?.FirstOrDefault(); + var badgeFileContent = badgeFile?.Content ?? throw new FileContentIsNullException($"File content retrieved from {url} is null"); try { @@ -100,5 +95,15 @@ private static string GetLastSegmentFromUrl(string url, out string parentFolderP return lastSegment; } + + private static (string org, string repo) GetRepoAndOrgNameFromUrl(string url) + { + var uri = new Uri(url); + string[] segments = uri.Segments; + string org = segments[1].TrimEnd('/'); + string repo = segments[2].TrimEnd('/'); + + return (org, repo); + } } } From 97931d3a041a36a28abb1d721bc5f6ce287dccff Mon Sep 17 00:00:00 2001 From: Zahra Ahangari Date: Wed, 29 Mar 2023 20:49:20 +0330 Subject: [PATCH 12/53] Remove unrelated validation in badge loading test --- .../GitHubBadgeServiceTests.cs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/CrystallineSociety/CrystallineSociety.Server.Api.Test/GitHubBadgeServiceTests.cs b/src/CrystallineSociety/CrystallineSociety.Server.Api.Test/GitHubBadgeServiceTests.cs index 57958e2f..f2675403 100644 --- a/src/CrystallineSociety/CrystallineSociety.Server.Api.Test/GitHubBadgeServiceTests.cs +++ b/src/CrystallineSociety/CrystallineSociety.Server.Api.Test/GitHubBadgeServiceTests.cs @@ -29,14 +29,6 @@ public async Task GitHubBadge_LoadSimple() Assert.IsNotNull(badge); - var bundle = new BadgeBundleDto(); - bundle.Badges.Add(badge); - - var badgeSystem = factory.CreateNew(bundle); - - Assert.IsNotNull(badgeSystem.Validations); - Assert.IsFalse(badgeSystem.Validations.Any()); - } [TestMethod] From 0d7759a34e0972bb4cc41f9d44bc2b032470754c Mon Sep 17 00:00:00 2001 From: BSeyfi <42473071+BSeyfi@users.noreply.github.com> Date: Thu, 30 Mar 2023 12:11:46 +0330 Subject: [PATCH 13/53] Added a new badge file. --- .../spec-badge.json | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 src/CrystallineSociety/Shared/CrystallineSociety.Shared.Test/BadgeSystem/SampleBadgeDocs/serialization-badge-sample/spec-badge.json diff --git a/src/CrystallineSociety/Shared/CrystallineSociety.Shared.Test/BadgeSystem/SampleBadgeDocs/serialization-badge-sample/spec-badge.json b/src/CrystallineSociety/Shared/CrystallineSociety.Shared.Test/BadgeSystem/SampleBadgeDocs/serialization-badge-sample/spec-badge.json new file mode 100644 index 00000000..6d89aadf --- /dev/null +++ b/src/CrystallineSociety/Shared/CrystallineSociety.Shared.Test/BadgeSystem/SampleBadgeDocs/serialization-badge-sample/spec-badge.json @@ -0,0 +1,59 @@ +{ + "code": "doc-guru-badge-sample", + "description": "Description for doc-guru-badge-sample", + "level": "Gold", + "info": { + "en": "info_en.md", + "fa": "info_fa.md" + }, + "appraisal-methods": [ + { + "title": "Main Method", + "badge-requirements": [ + "requirement-badge-code-A*2", + "requirement-badge-code-B", + "requirement-badge-code-C*1|requirement-badge-code-D*2" + ], + "activity-requirements": [ + "requirement-activity-code-A", + "requirement-activity-code-B*1", + "requirement-activity-code-C|requirement-activity-code-D*2" + ], + "approving-steps": [ + { + "step": 1, + "title": "Initial Approval", + "approver-required-badges": [ + "requirement-badge-code-A*2", + "requirement-badge-code-B", + "requirement-badge-code-C*2|requirement-badge-code-D" + ], + "required-approval-count": 2 + }, + { + "step": 2, + "title": "Final Approval", + "approver-required-badges": [ + "requirement-badge-code-D*2" + ], + "required-approval-count": 2 + } + ] + }, + { + "title": "Superpower Method", + "badge-requirements": [], + "activity-requirements": [], + "approving-steps": [ + { + "step": 1, + "title": "Superpower Admin", + "approver-required-badges": [ + "superpower" + ], + "required-approval-count": 1 + } + ] + } + ] +} \ No newline at end of file From b18446db014a24bf5f996e8aacfd32f07d54fded Mon Sep 17 00:00:00 2001 From: BSeyfi <42473071+BSeyfi@users.noreply.github.com> Date: Thu, 30 Mar 2023 12:32:54 +0330 Subject: [PATCH 14/53] Specify the exception type --- .../Services/Implementations/ServerGitHubBadgeService.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/CrystallineSociety/Server/Api/Services/Implementations/ServerGitHubBadgeService.cs b/src/CrystallineSociety/Server/Api/Services/Implementations/ServerGitHubBadgeService.cs index 2f7fb687..6e19781d 100644 --- a/src/CrystallineSociety/Server/Api/Services/Implementations/ServerGitHubBadgeService.cs +++ b/src/CrystallineSociety/Server/Api/Services/Implementations/ServerGitHubBadgeService.cs @@ -58,12 +58,10 @@ await client.Repository.GetAllForOrg(orgName) var folderContents = await client.Repository.Content.GetAllContents(repo.Id, folderPath); var badgeFilePath = folderContents.FirstOrDefault(x => x.Name.EndsWith("-badge.json"))?.Path - ?? throw new ResourceNotFoundException($"Unable to locate badge url: {url}"); - + ?? throw new FileNotFoundException($"Badge file not found in: {url}"); var contents = await client.Repository.Content.GetAllContents(repo.Id, badgeFilePath); - // ToDo: Dig a little deeper in each of these variables for being null - var badgeFile = contents?.FirstOrDefault(); - var badgeFileContent = badgeFile?.Content ?? throw new FileContentIsNullException($"File content retrieved from {url} is null"); + var badgeFile = contents!.First(); + var badgeFileContent = badgeFile.Content ?? throw new FileContentIsNullException($"File content retrieved from {url} is null"); try { From a0ce4e835e46d86b35b271cc590fe9079ba2d0c1 Mon Sep 17 00:00:00 2001 From: BSeyfi <42473071+BSeyfi@users.noreply.github.com> Date: Thu, 30 Mar 2023 12:34:14 +0330 Subject: [PATCH 15/53] Improve GetBadgeAsync documentation --- .../Shared/Shared/Services/Contracts/IGitHubBadgeService.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/CrystallineSociety/Shared/Shared/Services/Contracts/IGitHubBadgeService.cs b/src/CrystallineSociety/Shared/Shared/Services/Contracts/IGitHubBadgeService.cs index da455f92..20c02f01 100644 --- a/src/CrystallineSociety/Shared/Shared/Services/Contracts/IGitHubBadgeService.cs +++ b/src/CrystallineSociety/Shared/Shared/Services/Contracts/IGitHubBadgeService.cs @@ -19,10 +19,12 @@ public interface IGitHubBadgeService /// /// Loads a badge spec from the given and parses it, and returns a BadgeDto. /// - /// + /// The url address that contains badge files or folders containing badge file/folders. /// Returns the parsed badge. /// If the loaded url string is not in a correct format. /// If the given url could not be found. + /// If the given url contains no valid badge file. + /// If the badge file content is null. Task GetBadgeAsync(string url); } } From 3b2209d9b38fdca1a0b93d3166e454fdc786d075 Mon Sep 17 00:00:00 2001 From: BSeyfi <42473071+BSeyfi@users.noreply.github.com> Date: Thu, 30 Mar 2023 12:36:02 +0330 Subject: [PATCH 16/53] Add badge file --- .../CrystallineSociety.Shared.Test.csproj | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/CrystallineSociety/Shared/CrystallineSociety.Shared.Test/CrystallineSociety.Shared.Test.csproj b/src/CrystallineSociety/Shared/CrystallineSociety.Shared.Test/CrystallineSociety.Shared.Test.csproj index 62c9f522..e3d82f35 100644 --- a/src/CrystallineSociety/Shared/CrystallineSociety.Shared.Test/CrystallineSociety.Shared.Test.csproj +++ b/src/CrystallineSociety/Shared/CrystallineSociety.Shared.Test/CrystallineSociety.Shared.Test.csproj @@ -55,6 +55,9 @@ Always + + Always + Always From af8b10c9a12a0bf80dff9b96b2c07045b227519f Mon Sep 17 00:00:00 2001 From: Hootan HT Date: Thu, 30 Mar 2023 16:39:29 +0330 Subject: [PATCH 17/53] Fix repo auth with user name and fix request work with branch. --- .../GitHubBadgeServiceTests.cs | 4 +- .../ServerGitHubBadgeService.cs | 42 ++++++++++++++----- 2 files changed, 33 insertions(+), 13 deletions(-) diff --git a/src/CrystallineSociety/CrystallineSociety.Server.Api.Test/GitHubBadgeServiceTests.cs b/src/CrystallineSociety/CrystallineSociety.Server.Api.Test/GitHubBadgeServiceTests.cs index f2675403..322dc23a 100644 --- a/src/CrystallineSociety/CrystallineSociety.Server.Api.Test/GitHubBadgeServiceTests.cs +++ b/src/CrystallineSociety/CrystallineSociety.Server.Api.Test/GitHubBadgeServiceTests.cs @@ -24,7 +24,7 @@ public async Task GitHubBadge_LoadSimple() var factory = testHost.Services.GetRequiredService(); var badgeUrl = - "https://github.com/cs-internship/cs-system/tree/main/src/CrystallineSociety/Shared/CrystallineSociety.Shared.Test/BadgeSystem/SampleBadgeDocs/serialization-badge-sample"; + "https://github.com/hootanht/cs-system/tree/feature/initial-get-badge-system/src/CrystallineSociety/Shared/CrystallineSociety.Shared.Test/BadgeSystem/SampleBadgeDocs/serialization-badge-sample"; var badge = await githubService.GetBadgeAsync(badgeUrl); Assert.IsNotNull(badge); @@ -46,7 +46,7 @@ public async Task GitHubBadgesList_LoadSimple() var factory = testHost.Services.GetRequiredService(); var badgesFolderUrl = - "https://github.com/cs-internship/cs-system/tree/main/src/CrystallineSociety/Shared/CrystallineSociety.Shared.Test/BadgeSystem/SampleBadgeDocs/github-sample-folder"; + "https://github.com/cs-internship/cs-system/tree/feature/initial-get-badge-system/src/CrystallineSociety/Shared/CrystallineSociety.Shared.Test/BadgeSystem/SampleBadgeDocs/github-sample-folder"; var badges = await githubService.GetBadgesAsync(badgesFolderUrl); Assert.IsNotNull(badges); diff --git a/src/CrystallineSociety/Server/Api/Services/Implementations/ServerGitHubBadgeService.cs b/src/CrystallineSociety/Server/Api/Services/Implementations/ServerGitHubBadgeService.cs index 2f7fb687..b1a513a7 100644 --- a/src/CrystallineSociety/Server/Api/Services/Implementations/ServerGitHubBadgeService.cs +++ b/src/CrystallineSociety/Server/Api/Services/Implementations/ServerGitHubBadgeService.cs @@ -44,25 +44,32 @@ public async Task> GetBadgesAsync(string url) public async Task GetBadgeAsync(string url) { var client = new GitHubClient(new ProductHeaderValue("CS-System")); - var (orgName, repoName) = GetRepoAndOrgNameFromUrl(url); - var repos = - await client.Repository.GetAllForOrg(orgName) - ?? throw new ResourceNotFoundException($"Unable to locate orgName: {url}"); + var (orgName, repoName) = GetRepoAndOrgNameFromUrl(url); var repo = - repos.First(r => r.Name == repoName) - ?? throw new ResourceNotFoundException($"Unable to locate repoName: {url}"); + await client.Repository.Get(orgName, repoName) + ?? throw new ResourceNotFoundException($"Unable to locate orgName/repoName: {url}"); + + var branchName = GetBranchNameFromUrl(url); + + var refs = await client.Git.Reference.GetAll(repo.Id); + + var branchRef = refs.First(r => r.Ref.Contains($"refs/heads/{branchName}")); var folderPath = GetRelativeFolderPath(url); - var folderContents = await client.Repository.Content.GetAllContents(repo.Id, folderPath); + + var folderContents = await client.Repository.Content.GetAllContentsByRef(repo.Id, folderPath, branchRef.Ref); + var badgeFilePath = folderContents.FirstOrDefault(x => x.Name.EndsWith("-badge.json"))?.Path + ?? throw new ResourceNotFoundException($"Unable to locate badge url: {url}"); - var contents = await client.Repository.Content.GetAllContents(repo.Id, badgeFilePath); + var contents = await client.Repository.Content.GetAllContentsByRef(repo.Id, badgeFilePath,branchRef.Ref); // ToDo: Dig a little deeper in each of these variables for being null var badgeFile = contents?.FirstOrDefault(); + var badgeFileContent = badgeFile?.Content ?? throw new FileContentIsNullException($"File content retrieved from {url} is null"); try @@ -84,6 +91,19 @@ private static string GetRelativeFolderPath(string url) return folderPath; } + private static string GetBranchNameFromUrl(string url) + { + var treeIndex = url.IndexOf("/tree/", StringComparison.Ordinal); + + var branchName = url[(treeIndex + "/tree/".Length)..]; + + var slashIndex = branchName.IndexOf('/'); + + branchName = branchName[..slashIndex]; + + return branchName; + } + private static string GetLastSegmentFromUrl(string url, out string parentFolderPath) { var uri = new Uri(url); @@ -99,9 +119,9 @@ private static string GetLastSegmentFromUrl(string url, out string parentFolderP private static (string org, string repo) GetRepoAndOrgNameFromUrl(string url) { var uri = new Uri(url); - string[] segments = uri.Segments; - string org = segments[1].TrimEnd('/'); - string repo = segments[2].TrimEnd('/'); + var segments = uri.Segments; + var org = segments[1].TrimEnd('/'); + var repo = segments[2].TrimEnd('/'); return (org, repo); } From 2d3a55f0dad3d57553e87bb77e3eb08342ce315f Mon Sep 17 00:00:00 2001 From: BSeyfi <42473071+BSeyfi@users.noreply.github.com> Date: Thu, 30 Mar 2023 23:12:29 +0330 Subject: [PATCH 18/53] Improve `GetBranchNameFromUrl` method --- .../ServerGitHubBadgeService.cs | 51 +++++++++---------- 1 file changed, 25 insertions(+), 26 deletions(-) diff --git a/src/CrystallineSociety/Server/Api/Services/Implementations/ServerGitHubBadgeService.cs b/src/CrystallineSociety/Server/Api/Services/Implementations/ServerGitHubBadgeService.cs index d5e448f6..7288a25f 100644 --- a/src/CrystallineSociety/Server/Api/Services/Implementations/ServerGitHubBadgeService.cs +++ b/src/CrystallineSociety/Server/Api/Services/Implementations/ServerGitHubBadgeService.cs @@ -1,16 +1,14 @@ using System.Reflection.Metadata; using System.Text; - +using System.Text.RegularExpressions; using CrystallineSociety.Shared.Dtos.BadgeSystem; - using Octokit; namespace CrystallineSociety.Server.Api.Services.Implementations { public partial class ServerGitHubBadgeService : IGitHubBadgeService { - [AutoInject] - public IBadgeUtilService BadgeUtilService { get; set; } + [AutoInject] public IBadgeUtilService BadgeUtilService { get; set; } public async Task> GetBadgesAsync(string url) { @@ -38,6 +36,7 @@ public async Task> GetBadgesAsync(string url) var badgeContent = Encoding.UTF8.GetString(bytes); badges.Add(BadgeUtilService.ParseBadge(badgeContent)); } + return badges; } @@ -47,26 +46,23 @@ public async Task GetBadgeAsync(string url) var (orgName, repoName) = GetRepoAndOrgNameFromUrl(url); - var repo = - await client.Repository.Get(orgName, repoName) - ?? throw new ResourceNotFoundException($"Unable to locate orgName/repoName: {url}"); - - var branchName = GetBranchNameFromUrl(url); + var repo = await client.Repository.Get(orgName, repoName) + ?? throw new ResourceNotFoundException($"Unable to locate orgName/repoName: {url}"); var refs = await client.Git.Reference.GetAll(repo.Id); - + var branchName = GetBranchNameFromUrl(url, refs); var branchRef = refs.First(r => r.Ref.Contains($"refs/heads/{branchName}")); - var folderPath = GetRelativeFolderPath(url); - - var folderContents = await client.Repository.Content.GetAllContentsByRef(repo.Id, folderPath, branchRef.Ref); + var folderContents = + await client.Repository.Content.GetAllContentsByRef(repo.Id, folderPath, branchRef.Ref); var badgeFilePath = folderContents.FirstOrDefault(x => x.Name.EndsWith("-badge.json"))?.Path ?? throw new FileNotFoundException($"Badge file not found in: {url}"); - var contents = await client.Repository.Content.GetAllContentsByRef(repo.Id, badgeFilePath,branchRef.Ref); + var contents = await client.Repository.Content.GetAllContentsByRef(repo.Id, badgeFilePath, branchRef.Ref); var badgeFile = contents!.First(); - var badgeFileContent = badgeFile.Content ?? throw new FileContentIsNullException($"File content retrieved from {url} is null"); + var badgeFileContent = badgeFile.Content ?? + throw new FileContentIsNullException($"File content retrieved from {url} is null"); try { @@ -77,7 +73,6 @@ await client.Repository.Get(orgName, repoName) { throw new FormatException($"Can not parse badge with url: '{url}' ", exception); } - } private static string GetRelativeFolderPath(string url) @@ -87,17 +82,21 @@ private static string GetRelativeFolderPath(string url) return folderPath; } - private static string GetBranchNameFromUrl(string url) + private static string GetBranchNameFromUrl(string url, IReadOnlyList refs) { - var treeIndex = url.IndexOf("/tree/", StringComparison.Ordinal); - - var branchName = url[(treeIndex + "/tree/".Length)..]; - - var slashIndex = branchName.IndexOf('/'); - - branchName = branchName[..slashIndex]; + var uri = new Uri(url); + var afterTreeSegments = String.Join("", uri.Segments[4..]); + var branchInRefWithEndingSlash = ""; + foreach (var reference in refs) + { + branchInRefWithEndingSlash = $"{Regex.Replace(reference.Ref, @"^[^/]+/[^/]+/", "")}/"; + if (afterTreeSegments.StartsWith(branchInRefWithEndingSlash)) + { + break; + } + } - return branchName; + return branchInRefWithEndingSlash.TrimEnd('/'); } private static string GetLastSegmentFromUrl(string url, out string parentFolderPath) @@ -122,4 +121,4 @@ private static (string org, string repo) GetRepoAndOrgNameFromUrl(string url) return (org, repo); } } -} +} \ No newline at end of file From 559cec3c9c7798e570f0b788e29524d475237d3f Mon Sep 17 00:00:00 2001 From: BSeyfi <42473071+BSeyfi@users.noreply.github.com> Date: Thu, 30 Mar 2023 23:42:41 +0330 Subject: [PATCH 19/53] Throw exception when branch not found --- .../Implementations/ServerGitHubBadgeService.cs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/CrystallineSociety/Server/Api/Services/Implementations/ServerGitHubBadgeService.cs b/src/CrystallineSociety/Server/Api/Services/Implementations/ServerGitHubBadgeService.cs index 7288a25f..5a709a1c 100644 --- a/src/CrystallineSociety/Server/Api/Services/Implementations/ServerGitHubBadgeService.cs +++ b/src/CrystallineSociety/Server/Api/Services/Implementations/ServerGitHubBadgeService.cs @@ -50,7 +50,8 @@ public async Task GetBadgeAsync(string url) ?? throw new ResourceNotFoundException($"Unable to locate orgName/repoName: {url}"); var refs = await client.Git.Reference.GetAll(repo.Id); - var branchName = GetBranchNameFromUrl(url, refs); + var branchName = GetBranchNameFromUrl(url, refs) ?? + throw new ResourceNotFoundException($"Unable to locate branchName: {url}"); var branchRef = refs.First(r => r.Ref.Contains($"refs/heads/{branchName}")); var folderPath = GetRelativeFolderPath(url); var folderContents = @@ -82,21 +83,21 @@ private static string GetRelativeFolderPath(string url) return folderPath; } - private static string GetBranchNameFromUrl(string url, IReadOnlyList refs) + private static string? GetBranchNameFromUrl(string url, IReadOnlyList refs) { var uri = new Uri(url); var afterTreeSegments = String.Join("", uri.Segments[4..]); - var branchInRefWithEndingSlash = ""; foreach (var reference in refs) { - branchInRefWithEndingSlash = $"{Regex.Replace(reference.Ref, @"^[^/]+/[^/]+/", "")}/"; + + var branchInRefWithEndingSlash = $"{Regex.Replace(reference.Ref, @"^[^/]+/[^/]+/", "")}/"; if (afterTreeSegments.StartsWith(branchInRefWithEndingSlash)) { - break; + return branchInRefWithEndingSlash.TrimEnd('/'); } } - return branchInRefWithEndingSlash.TrimEnd('/'); + return null; } private static string GetLastSegmentFromUrl(string url, out string parentFolderPath) From 9ad06add3bc3769c5fc67ded2949a4ad309cf880 Mon Sep 17 00:00:00 2001 From: Vahid Moradpour Date: Sat, 1 Apr 2023 14:02:58 +0330 Subject: [PATCH 20/53] Add new json file --- .../serialization-badge-sample/spec-empty-badge.json | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/CrystallineSociety/Shared/CrystallineSociety.Shared.Test/BadgeSystem/SampleBadgeDocs/serialization-badge-sample/spec-empty-badge.json diff --git a/src/CrystallineSociety/Shared/CrystallineSociety.Shared.Test/BadgeSystem/SampleBadgeDocs/serialization-badge-sample/spec-empty-badge.json b/src/CrystallineSociety/Shared/CrystallineSociety.Shared.Test/BadgeSystem/SampleBadgeDocs/serialization-badge-sample/spec-empty-badge.json new file mode 100644 index 00000000..e69de29b From 0ecf9876d5d08324377760b90b25d56ee3abcd0e Mon Sep 17 00:00:00 2001 From: Vahid Moradpour Date: Sat, 1 Apr 2023 19:15:35 +0330 Subject: [PATCH 21/53] Complete GetBadgeAsync test scenarios --- .../GitHubBadgeServiceTests.cs | 102 +++++++++++++++++- .../ServerGitHubBadgeService.cs | 14 +-- .../CrystallineSociety.Shared.Test.csproj | 4 + 3 files changed, 112 insertions(+), 8 deletions(-) diff --git a/src/CrystallineSociety/CrystallineSociety.Server.Api.Test/GitHubBadgeServiceTests.cs b/src/CrystallineSociety/CrystallineSociety.Server.Api.Test/GitHubBadgeServiceTests.cs index 322dc23a..911c1d04 100644 --- a/src/CrystallineSociety/CrystallineSociety.Server.Api.Test/GitHubBadgeServiceTests.cs +++ b/src/CrystallineSociety/CrystallineSociety.Server.Api.Test/GitHubBadgeServiceTests.cs @@ -1,6 +1,6 @@ -using CrystallineSociety.Shared.Dtos.BadgeSystem; using CrystallineSociety.Shared.Services.Implementations.BadgeSystem; using Microsoft.Extensions.Hosting; +using Octokit; namespace CrystallineSociety.Server.Api.Test { @@ -55,5 +55,105 @@ public async Task GitHubBadgesList_LoadSimple() var badgeSystem = factory.CreateNew(badges); } + + [TestMethod] + public async Task GetBadgeAsync_IncorrectBranchName_ThrowResourceNotFoundException() + { + var testHost = Host.CreateDefaultBuilder() + .ConfigureServices((_, services) => + { + services.AddSharedServices(); + services.AddServerServices(); + } + ).Build(); + + var githubService = testHost.Services.GetRequiredService(); + + var badgeUrl = + "https://github.com/hootanht/cs-system/tree/feature-initial-get-badge-system/src/CrystallineSociety/Shared/CrystallineSociety.Shared.Test/BadgeSystem/SampleBadgeDocs/serialization-badge-sample"; + + var ex = await Assert.ThrowsExceptionAsync(async () => + { + await githubService.GetBadgeAsync(badgeUrl); + }); + + Assert.IsNotNull(ex); + Assert.AreEqual($"Unable to locate branchName: {badgeUrl}", ex.Message); + } + + [TestMethod] + public async Task GetBadgeAsync_IncorrectOrganizationOrRepositoryName_ThrowOctokitNotFoundException() + { + var testHost = Host.CreateDefaultBuilder() + .ConfigureServices((_, services) => + { + services.AddSharedServices(); + services.AddServerServices(); + } + ).Build(); + + var githubService = testHost.Services.GetRequiredService(); + + var badgeUrl = + "https://github.com/hootanhtbug/cs-system/tree/feature/initial-get-badge-system/src/CrystallineSociety/Shared/CrystallineSociety.Shared.Test/BadgeSystem/SampleBadgeDocs/serialization-badge-sample"; + + var ex = await Assert.ThrowsExceptionAsync(async () => + { + await githubService.GetBadgeAsync(badgeUrl); + }); + + Assert.IsNotNull(ex); + Assert.AreEqual($"Not Found", ex.Message); + } + + [TestMethod] + public async Task GetBadgeAsync_InvalidBadgeFileName_ThrowFileNotFoundException() + { + var testHost = Host.CreateDefaultBuilder() + .ConfigureServices((_, services) => + { + services.AddSharedServices(); + services.AddServerServices(); + } + ).Build(); + + var githubService = testHost.Services.GetRequiredService(); + + var badgeUrl = + "https://github.com/hootanht/cs-system/tree/feature/initial-get-badge-system/src/CrystallineSociety/Shared/CrystallineSociety.Shared.Test/BadgeSystem/SampleBadgeDocs/serialization-badge-sample/spec.json"; + + var exception = await Assert.ThrowsExceptionAsync(async () => + { + await githubService.GetBadgeAsync(badgeUrl); + }); + + Assert.IsNotNull(exception); + Assert.AreEqual($"Badge file not found in: {badgeUrl}", exception.Message); + } + + [TestMethod] + public async Task GetBadgeAsync_UnparsableBadgeFileFormat_ThrowFormatException() + { + var testHost = Host.CreateDefaultBuilder() + .ConfigureServices((_, services) => + { + services.AddSharedServices(); + services.AddServerServices(); + } + ).Build(); + + var githubService = testHost.Services.GetRequiredService(); + + var badgeUrl = + "https://github.com/hootanht/cs-system/tree/feature/initial-get-badge-system/src/CrystallineSociety/Shared/CrystallineSociety.Shared.Test/BadgeSystem/SampleBadgeDocs/serialization-badge-sample/spec-empty-badge.json"; + + var exception = await Assert.ThrowsExceptionAsync(async () => + { + await githubService.GetBadgeAsync(badgeUrl); + }); + + Assert.IsNotNull(exception); + Assert.AreEqual($"Can not parse badge with url: '{badgeUrl}'", exception.Message); + } } } \ No newline at end of file diff --git a/src/CrystallineSociety/Server/Api/Services/Implementations/ServerGitHubBadgeService.cs b/src/CrystallineSociety/Server/Api/Services/Implementations/ServerGitHubBadgeService.cs index 5a709a1c..e18f9707 100644 --- a/src/CrystallineSociety/Server/Api/Services/Implementations/ServerGitHubBadgeService.cs +++ b/src/CrystallineSociety/Server/Api/Services/Implementations/ServerGitHubBadgeService.cs @@ -46,8 +46,7 @@ public async Task GetBadgeAsync(string url) var (orgName, repoName) = GetRepoAndOrgNameFromUrl(url); - var repo = await client.Repository.Get(orgName, repoName) - ?? throw new ResourceNotFoundException($"Unable to locate orgName/repoName: {url}"); + var repo = await client.Repository.Get(orgName, repoName); var refs = await client.Git.Reference.GetAll(repo.Id); var branchName = GetBranchNameFromUrl(url, refs) ?? @@ -62,9 +61,9 @@ public async Task GetBadgeAsync(string url) ?? throw new FileNotFoundException($"Badge file not found in: {url}"); var contents = await client.Repository.Content.GetAllContentsByRef(repo.Id, badgeFilePath, branchRef.Ref); var badgeFile = contents!.First(); - var badgeFileContent = badgeFile.Content ?? - throw new FileContentIsNullException($"File content retrieved from {url} is null"); - + + var badgeFileContent = badgeFile.Content; + try { var badge = BadgeUtilService.ParseBadge(badgeFileContent); @@ -72,8 +71,9 @@ public async Task GetBadgeAsync(string url) } catch (Exception exception) { - throw new FormatException($"Can not parse badge with url: '{url}' ", exception); + throw new FormatException($"Can not parse badge with url: '{url}'", exception); } + } private static string GetRelativeFolderPath(string url) @@ -89,7 +89,7 @@ private static string GetRelativeFolderPath(string url) var afterTreeSegments = String.Join("", uri.Segments[4..]); foreach (var reference in refs) { - + var branchInRefWithEndingSlash = $"{Regex.Replace(reference.Ref, @"^[^/]+/[^/]+/", "")}/"; if (afterTreeSegments.StartsWith(branchInRefWithEndingSlash)) { diff --git a/src/CrystallineSociety/Shared/CrystallineSociety.Shared.Test/CrystallineSociety.Shared.Test.csproj b/src/CrystallineSociety/Shared/CrystallineSociety.Shared.Test/CrystallineSociety.Shared.Test.csproj index e3d82f35..53878c5c 100644 --- a/src/CrystallineSociety/Shared/CrystallineSociety.Shared.Test/CrystallineSociety.Shared.Test.csproj +++ b/src/CrystallineSociety/Shared/CrystallineSociety.Shared.Test/CrystallineSociety.Shared.Test.csproj @@ -18,6 +18,7 @@ + @@ -55,6 +56,9 @@ Always + + Always + Always From 7c735fb0e53e95c1e22b0678f683ce65731dd41b Mon Sep 17 00:00:00 2001 From: Hootan HT Date: Sat, 1 Apr 2023 21:13:36 +0330 Subject: [PATCH 22/53] Add TestInitialize and configurationService to Test project. --- .../GitHubBadgeServiceTests.cs | 107 +++++++----------- .../appsettings.json | 5 + 2 files changed, 49 insertions(+), 63 deletions(-) create mode 100644 src/CrystallineSociety/CrystallineSociety.Server.Api.Test/appsettings.json diff --git a/src/CrystallineSociety/CrystallineSociety.Server.Api.Test/GitHubBadgeServiceTests.cs b/src/CrystallineSociety/CrystallineSociety.Server.Api.Test/GitHubBadgeServiceTests.cs index 911c1d04..80647a33 100644 --- a/src/CrystallineSociety/CrystallineSociety.Server.Api.Test/GitHubBadgeServiceTests.cs +++ b/src/CrystallineSociety/CrystallineSociety.Server.Api.Test/GitHubBadgeServiceTests.cs @@ -1,5 +1,8 @@ using CrystallineSociety.Shared.Services.Implementations.BadgeSystem; + +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Hosting; + using Octokit; namespace CrystallineSociety.Server.Api.Test @@ -9,22 +12,41 @@ public class GitHubBadgeServiceTests { public TestContext TestContext { get; set; } = default!; + private IHost _testHost = default!; + + private string? TestOrgName { get; set; } + + private string? TestRepoName { get; set; } + + private string? TestBranchName { get; set; } + + [TestInitialize] + public void TestInitialize() + { + _testHost = Host.CreateDefaultBuilder() + .ConfigureServices((_, services) => + { + services.AddSharedServices(); + services.AddServerServices(); + } + ).Build(); + + var configurationService = _testHost.Services.GetRequiredService(); + + TestOrgName = configurationService.GetSection("OrganizationName").Value; + + TestRepoName = configurationService.GetSection("RepositoryName").Value; + + TestBranchName = configurationService.GetSection("BranchName").Value; + } + [TestMethod] public async Task GitHubBadge_LoadSimple() { - var testHost = Host.CreateDefaultBuilder() - .ConfigureServices((_, services) => - { - services.AddSharedServices(); - services.AddServerServices(); - } - ).Build(); - - var githubService = testHost.Services.GetRequiredService(); - var factory = testHost.Services.GetRequiredService(); + var githubService = _testHost.Services.GetRequiredService(); var badgeUrl = - "https://github.com/hootanht/cs-system/tree/feature/initial-get-badge-system/src/CrystallineSociety/Shared/CrystallineSociety.Shared.Test/BadgeSystem/SampleBadgeDocs/serialization-badge-sample"; + $"https://github.com/{TestOrgName}/{TestRepoName}/tree/{TestBranchName}/src/CrystallineSociety/Shared/CrystallineSociety.Shared.Test/BadgeSystem/SampleBadgeDocs/serialization-badge-sample"; var badge = await githubService.GetBadgeAsync(badgeUrl); Assert.IsNotNull(badge); @@ -34,43 +56,26 @@ public async Task GitHubBadge_LoadSimple() [TestMethod] public async Task GitHubBadgesList_LoadSimple() { - var testHost = Host.CreateDefaultBuilder() - .ConfigureServices((_, services) => - { - services.AddSharedServices(); - services.AddServerServices(); - } - ).Build(); - - var githubService = testHost.Services.GetRequiredService(); - var factory = testHost.Services.GetRequiredService(); + var githubService = _testHost.Services.GetRequiredService(); + var factory = _testHost.Services.GetRequiredService(); var badgesFolderUrl = - "https://github.com/cs-internship/cs-system/tree/feature/initial-get-badge-system/src/CrystallineSociety/Shared/CrystallineSociety.Shared.Test/BadgeSystem/SampleBadgeDocs/github-sample-folder"; + $"https://github.com/{TestOrgName}/{TestRepoName}/tree/{TestBranchName}/src/CrystallineSociety/Shared/CrystallineSociety.Shared.Test/BadgeSystem/SampleBadgeDocs/github-sample-folder"; var badges = await githubService.GetBadgesAsync(badgesFolderUrl); Assert.IsNotNull(badges); Assert.AreEqual(6, badges.Count); var badgeSystem = factory.CreateNew(badges); - } [TestMethod] public async Task GetBadgeAsync_IncorrectBranchName_ThrowResourceNotFoundException() { - var testHost = Host.CreateDefaultBuilder() - .ConfigureServices((_, services) => - { - services.AddSharedServices(); - services.AddServerServices(); - } - ).Build(); - - var githubService = testHost.Services.GetRequiredService(); + var githubService = _testHost.Services.GetRequiredService(); var badgeUrl = - "https://github.com/hootanht/cs-system/tree/feature-initial-get-badge-system/src/CrystallineSociety/Shared/CrystallineSociety.Shared.Test/BadgeSystem/SampleBadgeDocs/serialization-badge-sample"; + $"https://github.com/{TestOrgName}/{TestRepoName}/tree/{TestBranchName}/src/CrystallineSociety/Shared/CrystallineSociety.Shared.Test/BadgeSystem/SampleBadgeDocs/serialization-badge-sample"; var ex = await Assert.ThrowsExceptionAsync(async () => { @@ -84,18 +89,10 @@ public async Task GetBadgeAsync_IncorrectBranchName_ThrowResourceNotFoundExcepti [TestMethod] public async Task GetBadgeAsync_IncorrectOrganizationOrRepositoryName_ThrowOctokitNotFoundException() { - var testHost = Host.CreateDefaultBuilder() - .ConfigureServices((_, services) => - { - services.AddSharedServices(); - services.AddServerServices(); - } - ).Build(); - - var githubService = testHost.Services.GetRequiredService(); + var githubService = _testHost.Services.GetRequiredService(); var badgeUrl = - "https://github.com/hootanhtbug/cs-system/tree/feature/initial-get-badge-system/src/CrystallineSociety/Shared/CrystallineSociety.Shared.Test/BadgeSystem/SampleBadgeDocs/serialization-badge-sample"; + $"https://github.com/{TestOrgName}/{TestRepoName}/tree/{TestBranchName}/src/CrystallineSociety/Shared/CrystallineSociety.Shared.Test/BadgeSystem/SampleBadgeDocs/serialization-badge-sample"; var ex = await Assert.ThrowsExceptionAsync(async () => { @@ -109,18 +106,10 @@ public async Task GetBadgeAsync_IncorrectOrganizationOrRepositoryName_ThrowOctok [TestMethod] public async Task GetBadgeAsync_InvalidBadgeFileName_ThrowFileNotFoundException() { - var testHost = Host.CreateDefaultBuilder() - .ConfigureServices((_, services) => - { - services.AddSharedServices(); - services.AddServerServices(); - } - ).Build(); - - var githubService = testHost.Services.GetRequiredService(); + var githubService = _testHost.Services.GetRequiredService(); var badgeUrl = - "https://github.com/hootanht/cs-system/tree/feature/initial-get-badge-system/src/CrystallineSociety/Shared/CrystallineSociety.Shared.Test/BadgeSystem/SampleBadgeDocs/serialization-badge-sample/spec.json"; + $"https://github.com/{TestOrgName}/{TestRepoName}/tree/{TestBranchName}/src/CrystallineSociety/Shared/CrystallineSociety.Shared.Test/BadgeSystem/SampleBadgeDocs/serialization-badge-sample/spec.json"; var exception = await Assert.ThrowsExceptionAsync(async () => { @@ -134,18 +123,10 @@ public async Task GetBadgeAsync_InvalidBadgeFileName_ThrowFileNotFoundException( [TestMethod] public async Task GetBadgeAsync_UnparsableBadgeFileFormat_ThrowFormatException() { - var testHost = Host.CreateDefaultBuilder() - .ConfigureServices((_, services) => - { - services.AddSharedServices(); - services.AddServerServices(); - } - ).Build(); - - var githubService = testHost.Services.GetRequiredService(); + var githubService = _testHost.Services.GetRequiredService(); var badgeUrl = - "https://github.com/hootanht/cs-system/tree/feature/initial-get-badge-system/src/CrystallineSociety/Shared/CrystallineSociety.Shared.Test/BadgeSystem/SampleBadgeDocs/serialization-badge-sample/spec-empty-badge.json"; + $"https://github.com/{TestOrgName}/{TestRepoName}/tree/{TestBranchName}/src/CrystallineSociety/Shared/CrystallineSociety.Shared.Test/BadgeSystem/SampleBadgeDocs/serialization-badge-sample/spec-empty-badge.json"; var exception = await Assert.ThrowsExceptionAsync(async () => { diff --git a/src/CrystallineSociety/CrystallineSociety.Server.Api.Test/appsettings.json b/src/CrystallineSociety/CrystallineSociety.Server.Api.Test/appsettings.json new file mode 100644 index 00000000..a05a8708 --- /dev/null +++ b/src/CrystallineSociety/CrystallineSociety.Server.Api.Test/appsettings.json @@ -0,0 +1,5 @@ +{ + "OrganizationName": "hootanht", + "RepositoryName": "cs-system", + "BranchName": "feature/initial-get-badge-system" +} From 68f04e9bca852d7449a8b42ee7437e906b987627 Mon Sep 17 00:00:00 2001 From: Mehran Davoudi Date: Tue, 4 Apr 2023 00:10:55 +0330 Subject: [PATCH 23/53] Refactor GitHubService signatures --- .../IServiceCollectionExtensions.cs | 2 +- ...bBadgeService.cs => GitHubBadgeService.cs} | 81 ++++++++++++------- .../Shared/Dtos/BadgeSystem/BadgeDto.cs | 15 ++-- .../Services/Contracts/IGitHubBadgeService.cs | 16 ++-- 4 files changed, 68 insertions(+), 46 deletions(-) rename src/CrystallineSociety/Server/Api/Services/Implementations/{ServerGitHubBadgeService.cs => GitHubBadgeService.cs} (54%) diff --git a/src/CrystallineSociety/Server/Api/Extensions/IServiceCollectionExtensions.cs b/src/CrystallineSociety/Server/Api/Extensions/IServiceCollectionExtensions.cs index 4d3036d4..55387065 100644 --- a/src/CrystallineSociety/Server/Api/Extensions/IServiceCollectionExtensions.cs +++ b/src/CrystallineSociety/Server/Api/Extensions/IServiceCollectionExtensions.cs @@ -16,7 +16,7 @@ public static class IServiceCollectionExtensions { public static void AddServerServices(this IServiceCollection services) { - services.AddTransient(); + services.AddTransient(); services.AddAppHook(); services.AddTransient(); } diff --git a/src/CrystallineSociety/Server/Api/Services/Implementations/ServerGitHubBadgeService.cs b/src/CrystallineSociety/Server/Api/Services/Implementations/GitHubBadgeService.cs similarity index 54% rename from src/CrystallineSociety/Server/Api/Services/Implementations/ServerGitHubBadgeService.cs rename to src/CrystallineSociety/Server/Api/Services/Implementations/GitHubBadgeService.cs index e18f9707..7d90f1f8 100644 --- a/src/CrystallineSociety/Server/Api/Services/Implementations/ServerGitHubBadgeService.cs +++ b/src/CrystallineSociety/Server/Api/Services/Implementations/GitHubBadgeService.cs @@ -6,60 +6,79 @@ namespace CrystallineSociety.Server.Api.Services.Implementations { - public partial class ServerGitHubBadgeService : IGitHubBadgeService + public partial class GitHubBadgeService : IGitHubBadgeService { [AutoInject] public IBadgeUtilService BadgeUtilService { get; set; } - - public async Task> GetBadgesAsync(string url) + [AutoInject] public GitHubClient GitHubClient { get; set; } + public async Task> GetBadgesAsync(string folderUrl) { - var client = new GitHubClient(new ProductHeaderValue("CS-System")); - var repos = await client.Repository.GetAllForOrg("cs-internship"); - var repo = repos.First(r => r.Name == "cs-system"); - var lastSegment = GetLastSegmentFromUrl(url, out var parentFolderPath); - var folderContents = await client.Repository.Content.GetAllContents(repo.Id, parentFolderPath); - var destinationFolderSha = folderContents?.First(f => f.Name == lastSegment).Sha; - var destinationFolderContents = await client.Git.Tree.GetRecursive(repo.Id, destinationFolderSha); + var lightBadges = await GetLightBadgesAsync(folderUrl); var badges = new List(); - foreach (var item in destinationFolderContents.Tree) + foreach (var lightBadge in lightBadges) { - if (Path.GetExtension(item.Path) != ".json") + if (Path.GetExtension(lightBadge.Url) != ".json") continue; - var badgeBlob = await client.Git.Blob.Get(repo.Id, item.Sha); - - if (badgeBlob.Encoding != EncodingType.Base64) - continue; + var badgeDto = await GetBadgeAsync( + lightBadge.RepoId ?? throw new Exception("RepoId of light badge is null."), + lightBadge.Sha ?? throw new Exception("Sha of light badge is null.") + ); - var bytes = Convert.FromBase64String(badgeBlob.Content); - var badgeContent = Encoding.UTF8.GetString(bytes); - badges.Add(BadgeUtilService.ParseBadge(badgeContent)); + badges.Add(badgeDto); } return badges; } - public async Task GetBadgeAsync(string url) + public async Task> GetLightBadgesAsync(string url) + { + var repos = await GitHubClient.Repository.GetAllForOrg("cs-internship"); + var repo = repos.First(r => r.Name == "cs-system"); + var lastSegment = GetLastSegmentFromUrl(url, out var parentFolderPath); + var repositoryId = repo.Id; + var folderContents = await GitHubClient.Repository.Content.GetAllContents(repositoryId, parentFolderPath); + var folderSha = folderContents?.First(f => f.Name == lastSegment).Sha; + var allContents = await GitHubClient.Git.Tree.GetRecursive(repositoryId, folderSha); + return allContents.Tree + .Select(t => new BadgeDto { RepoId = repositoryId, Sha = t.Sha, Url = t.Url }) + .ToList(); + } + + public async Task GetBadgeAsync(long repositoryId, string sha) + { + var badgeBlob = await GitHubClient.Git.Blob.Get(repositoryId, sha); + + var bytes = Convert.FromBase64String(badgeBlob.Content); + var badgeContent = Encoding.UTF8.GetString(bytes); + var badgeDto = BadgeUtilService.ParseBadge(badgeContent); + return badgeDto; + } + + private static GitHubClient CreateClient() { - var client = new GitHubClient(new ProductHeaderValue("CS-System")); + return new GitHubClient(new ProductHeaderValue("CS-System")); + } - var (orgName, repoName) = GetRepoAndOrgNameFromUrl(url); + public async Task GetBadgeAsync(string badgeUrl) + { + var (orgName, repoName) = GetRepoAndOrgNameFromUrl(badgeUrl); - var repo = await client.Repository.Get(orgName, repoName); + var repo = await GitHubClient.Repository.Get(orgName, repoName); - var refs = await client.Git.Reference.GetAll(repo.Id); - var branchName = GetBranchNameFromUrl(url, refs) ?? - throw new ResourceNotFoundException($"Unable to locate branchName: {url}"); + var refs = await GitHubClient.Git.Reference.GetAll(repo.Id); + var branchName = GetBranchNameFromUrl(badgeUrl, refs) ?? + throw new ResourceNotFoundException($"Unable to locate branchName: {badgeUrl}"); var branchRef = refs.First(r => r.Ref.Contains($"refs/heads/{branchName}")); - var folderPath = GetRelativeFolderPath(url); + var folderPath = GetRelativeFolderPath(badgeUrl); var folderContents = - await client.Repository.Content.GetAllContentsByRef(repo.Id, folderPath, branchRef.Ref); + await GitHubClient.Repository.Content.GetAllContentsByRef(repo.Id, folderPath, branchRef.Ref); var badgeFilePath = folderContents.FirstOrDefault(x => x.Name.EndsWith("-badge.json"))?.Path - ?? throw new FileNotFoundException($"Badge file not found in: {url}"); - var contents = await client.Repository.Content.GetAllContentsByRef(repo.Id, badgeFilePath, branchRef.Ref); + ?? throw new FileNotFoundException($"Badge file not found in: {badgeUrl}"); + var contents = await GitHubClient.Repository.Content.GetAllContentsByRef(repo.Id, badgeFilePath, branchRef.Ref); var badgeFile = contents!.First(); var badgeFileContent = badgeFile.Content; @@ -71,7 +90,7 @@ public async Task GetBadgeAsync(string url) } catch (Exception exception) { - throw new FormatException($"Can not parse badge with url: '{url}'", exception); + throw new FormatException($"Can not parse badge with badgeUrl: '{badgeUrl}'", exception); } } diff --git a/src/CrystallineSociety/Shared/Shared/Dtos/BadgeSystem/BadgeDto.cs b/src/CrystallineSociety/Shared/Shared/Dtos/BadgeSystem/BadgeDto.cs index 5952581b..01468015 100644 --- a/src/CrystallineSociety/Shared/Shared/Dtos/BadgeSystem/BadgeDto.cs +++ b/src/CrystallineSociety/Shared/Shared/Dtos/BadgeSystem/BadgeDto.cs @@ -9,15 +9,18 @@ namespace CrystallineSociety.Shared.Dtos.BadgeSystem { public class BadgeDto { - public string Code { get; set; } = default!; + public string? Code { get; set; } = default!; public string? Description { get; set; } - public BadgeLevel Level { get; set; } - public Dictionary Info { get; set; } = new(); - public List AppraisalMethods { get; set; } = new(); - + public BadgeLevel? Level { get; set; } + public Dictionary? Info { get; set; } = new(); + public List? AppraisalMethods { get; set; } = new(); + + public string? Sha { get; set; } + public string? Url { get; set; } + public long? RepoId { get; set; } public override string ToString() { - return Code; + return Code ?? ""; } } diff --git a/src/CrystallineSociety/Shared/Shared/Services/Contracts/IGitHubBadgeService.cs b/src/CrystallineSociety/Shared/Shared/Services/Contracts/IGitHubBadgeService.cs index 20c02f01..df324efe 100644 --- a/src/CrystallineSociety/Shared/Shared/Services/Contracts/IGitHubBadgeService.cs +++ b/src/CrystallineSociety/Shared/Shared/Services/Contracts/IGitHubBadgeService.cs @@ -12,19 +12,19 @@ public interface IGitHubBadgeService /// /// /// - /// + /// /// - Task> GetBadgesAsync(string url); + Task> GetBadgesAsync(string folderUrl); /// - /// Loads a badge spec from the given and parses it, and returns a BadgeDto. + /// Loads a badge spec from the given and parses it, and returns a BadgeDto. /// - /// The url address that contains badge files or folders containing badge file/folders. + /// The badgeUrl address that contains badge files or folders containing badge file/folders. /// Returns the parsed badge. - /// If the loaded url string is not in a correct format. - /// If the given url could not be found. - /// If the given url contains no valid badge file. + /// If the loaded badgeUrl string is not in a correct format. + /// If the given badgeUrl could not be found. + /// If the given badgeUrl contains no valid badge file. /// If the badge file content is null. - Task GetBadgeAsync(string url); + Task GetBadgeAsync(string badgeUrl); } } From dfad82ad347b41dddec0ed746cb7f45339f539ca Mon Sep 17 00:00:00 2001 From: Zahra Ahangari Date: Tue, 4 Apr 2023 16:20:53 +0330 Subject: [PATCH 24/53] Refactor GitHubBadgeService after first review --- .../GitHubBadgeServiceTests.cs | 18 ++++++------------ .../appsettings.json | 5 ----- .../IServiceCollectionExtensions.cs | 3 +++ .../Implementations/GitHubBadgeService.cs | 19 ++++++++++++++----- .../Services/Contracts/IGitHubBadgeService.cs | 5 +++++ 5 files changed, 28 insertions(+), 22 deletions(-) delete mode 100644 src/CrystallineSociety/CrystallineSociety.Server.Api.Test/appsettings.json diff --git a/src/CrystallineSociety/CrystallineSociety.Server.Api.Test/GitHubBadgeServiceTests.cs b/src/CrystallineSociety/CrystallineSociety.Server.Api.Test/GitHubBadgeServiceTests.cs index 80647a33..eb394a64 100644 --- a/src/CrystallineSociety/CrystallineSociety.Server.Api.Test/GitHubBadgeServiceTests.cs +++ b/src/CrystallineSociety/CrystallineSociety.Server.Api.Test/GitHubBadgeServiceTests.cs @@ -14,12 +14,6 @@ public class GitHubBadgeServiceTests private IHost _testHost = default!; - private string? TestOrgName { get; set; } - - private string? TestRepoName { get; set; } - - private string? TestBranchName { get; set; } - [TestInitialize] public void TestInitialize() { @@ -46,7 +40,7 @@ public async Task GitHubBadge_LoadSimple() var githubService = _testHost.Services.GetRequiredService(); var badgeUrl = - $"https://github.com/{TestOrgName}/{TestRepoName}/tree/{TestBranchName}/src/CrystallineSociety/Shared/CrystallineSociety.Shared.Test/BadgeSystem/SampleBadgeDocs/serialization-badge-sample"; + "https://github.com/hootanht/cs-system/tree/feature/initial-get-badge-system/src/CrystallineSociety/Shared/CrystallineSociety.Shared.Test/BadgeSystem/SampleBadgeDocs/serialization-badge-sample"; var badge = await githubService.GetBadgeAsync(badgeUrl); Assert.IsNotNull(badge); @@ -60,7 +54,7 @@ public async Task GitHubBadgesList_LoadSimple() var factory = _testHost.Services.GetRequiredService(); var badgesFolderUrl = - $"https://github.com/{TestOrgName}/{TestRepoName}/tree/{TestBranchName}/src/CrystallineSociety/Shared/CrystallineSociety.Shared.Test/BadgeSystem/SampleBadgeDocs/github-sample-folder"; + "https://github.com/cs-internship/cs-system/tree/feature/initial-get-badge-system/src/CrystallineSociety/Shared/CrystallineSociety.Shared.Test/BadgeSystem/SampleBadgeDocs/github-sample-folder"; var badges = await githubService.GetBadgesAsync(badgesFolderUrl); Assert.IsNotNull(badges); @@ -75,7 +69,7 @@ public async Task GetBadgeAsync_IncorrectBranchName_ThrowResourceNotFoundExcepti var githubService = _testHost.Services.GetRequiredService(); var badgeUrl = - $"https://github.com/{TestOrgName}/{TestRepoName}/tree/{TestBranchName}/src/CrystallineSociety/Shared/CrystallineSociety.Shared.Test/BadgeSystem/SampleBadgeDocs/serialization-badge-sample"; + "https://github.com/hootanht/cs-system/tree/feature-initial-get-badge-system/src/CrystallineSociety/Shared/CrystallineSociety.Shared.Test/BadgeSystem/SampleBadgeDocs/serialization-badge-sample"; var ex = await Assert.ThrowsExceptionAsync(async () => { @@ -92,7 +86,7 @@ public async Task GetBadgeAsync_IncorrectOrganizationOrRepositoryName_ThrowOctok var githubService = _testHost.Services.GetRequiredService(); var badgeUrl = - $"https://github.com/{TestOrgName}/{TestRepoName}/tree/{TestBranchName}/src/CrystallineSociety/Shared/CrystallineSociety.Shared.Test/BadgeSystem/SampleBadgeDocs/serialization-badge-sample"; + "https://github.com/hootanhtbug/cs-system/tree/feature/initial-get-badge-system/src/CrystallineSociety/Shared/CrystallineSociety.Shared.Test/BadgeSystem/SampleBadgeDocs/serialization-badge-sample"; var ex = await Assert.ThrowsExceptionAsync(async () => { @@ -109,7 +103,7 @@ public async Task GetBadgeAsync_InvalidBadgeFileName_ThrowFileNotFoundException( var githubService = _testHost.Services.GetRequiredService(); var badgeUrl = - $"https://github.com/{TestOrgName}/{TestRepoName}/tree/{TestBranchName}/src/CrystallineSociety/Shared/CrystallineSociety.Shared.Test/BadgeSystem/SampleBadgeDocs/serialization-badge-sample/spec.json"; + "https://github.com/hootanht/cs-system/tree/feature/initial-get-badge-system/src/CrystallineSociety/Shared/CrystallineSociety.Shared.Test/BadgeSystem/SampleBadgeDocs/serialization-badge-sample/spec.json"; var exception = await Assert.ThrowsExceptionAsync(async () => { @@ -126,7 +120,7 @@ public async Task GetBadgeAsync_UnparsableBadgeFileFormat_ThrowFormatException() var githubService = _testHost.Services.GetRequiredService(); var badgeUrl = - $"https://github.com/{TestOrgName}/{TestRepoName}/tree/{TestBranchName}/src/CrystallineSociety/Shared/CrystallineSociety.Shared.Test/BadgeSystem/SampleBadgeDocs/serialization-badge-sample/spec-empty-badge.json"; + "https://github.com/hootanht/cs-system/tree/feature/initial-get-badge-system/src/CrystallineSociety/Shared/CrystallineSociety.Shared.Test/BadgeSystem/SampleBadgeDocs/serialization-badge-sample/spec-empty-badge.json"; var exception = await Assert.ThrowsExceptionAsync(async () => { diff --git a/src/CrystallineSociety/CrystallineSociety.Server.Api.Test/appsettings.json b/src/CrystallineSociety/CrystallineSociety.Server.Api.Test/appsettings.json deleted file mode 100644 index a05a8708..00000000 --- a/src/CrystallineSociety/CrystallineSociety.Server.Api.Test/appsettings.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "OrganizationName": "hootanht", - "RepositoryName": "cs-system", - "BranchName": "feature/initial-get-badge-system" -} diff --git a/src/CrystallineSociety/Server/Api/Extensions/IServiceCollectionExtensions.cs b/src/CrystallineSociety/Server/Api/Extensions/IServiceCollectionExtensions.cs index 55387065..b929c1fb 100644 --- a/src/CrystallineSociety/Server/Api/Extensions/IServiceCollectionExtensions.cs +++ b/src/CrystallineSociety/Server/Api/Extensions/IServiceCollectionExtensions.cs @@ -9,6 +9,7 @@ using CrystallineSociety.Server.Api.AppHooks; using CrystallineSociety.Server.Api.Models.Account; using CrystallineSociety.Server.Api.Services.Implementations; +using Octokit; namespace Microsoft.Extensions.DependencyInjection; @@ -19,6 +20,8 @@ public static void AddServerServices(this IServiceCollection services) services.AddTransient(); services.AddAppHook(); services.AddTransient(); + // ToDo: Complete. + services.AddTransient(); } public static void AddIdentity(this IServiceCollection services, IConfiguration configuration) diff --git a/src/CrystallineSociety/Server/Api/Services/Implementations/GitHubBadgeService.cs b/src/CrystallineSociety/Server/Api/Services/Implementations/GitHubBadgeService.cs index 7d90f1f8..7a287bfa 100644 --- a/src/CrystallineSociety/Server/Api/Services/Implementations/GitHubBadgeService.cs +++ b/src/CrystallineSociety/Server/Api/Services/Implementations/GitHubBadgeService.cs @@ -10,15 +10,17 @@ public partial class GitHubBadgeService : IGitHubBadgeService { [AutoInject] public IBadgeUtilService BadgeUtilService { get; set; } [AutoInject] public GitHubClient GitHubClient { get; set; } + public async Task> GetBadgesAsync(string folderUrl) { var lightBadges = await GetLightBadgesAsync(folderUrl); var badges = new List(); + // ToDo: Ask about throwing an exception for any problematic item or just pass to the next item? foreach (var lightBadge in lightBadges) { - if (Path.GetExtension(lightBadge.Url) != ".json") + if (lightBadge.Url is null || !lightBadge.Url.EndsWith("-badge.json")) continue; var badgeDto = await GetBadgeAsync( @@ -34,13 +36,18 @@ public async Task> GetBadgesAsync(string folderUrl) public async Task> GetLightBadgesAsync(string url) { - var repos = await GitHubClient.Repository.GetAllForOrg("cs-internship"); - var repo = repos.First(r => r.Name == "cs-system"); + var (orgName, repoName) = GetRepoAndOrgNameFromUrl(url); + var repos = await GitHubClient.Repository.GetAllForOrg(orgName); + var repo = repos.First(r => r.Name == repoName); + var lastSegment = GetLastSegmentFromUrl(url, out var parentFolderPath); var repositoryId = repo.Id; + + // ToDo: Handle if the given url is a file url instead of a folder url which is required here. var folderContents = await GitHubClient.Repository.Content.GetAllContents(repositoryId, parentFolderPath); var folderSha = folderContents?.First(f => f.Name == lastSegment).Sha; var allContents = await GitHubClient.Git.Tree.GetRecursive(repositoryId, folderSha); + return allContents.Tree .Select(t => new BadgeDto { RepoId = repositoryId, Sha = t.Sha, Url = t.Url }) .ToList(); @@ -64,12 +71,12 @@ private static GitHubClient CreateClient() public async Task GetBadgeAsync(string badgeUrl) { var (orgName, repoName) = GetRepoAndOrgNameFromUrl(badgeUrl); - var repo = await GitHubClient.Repository.Get(orgName, repoName); var refs = await GitHubClient.Git.Reference.GetAll(repo.Id); var branchName = GetBranchNameFromUrl(badgeUrl, refs) ?? throw new ResourceNotFoundException($"Unable to locate branchName: {badgeUrl}"); + var branchRef = refs.First(r => r.Ref.Contains($"refs/heads/{branchName}")); var folderPath = GetRelativeFolderPath(badgeUrl); var folderContents = @@ -78,6 +85,7 @@ public async Task GetBadgeAsync(string badgeUrl) var badgeFilePath = folderContents.FirstOrDefault(x => x.Name.EndsWith("-badge.json"))?.Path ?? throw new FileNotFoundException($"Badge file not found in: {badgeUrl}"); + var contents = await GitHubClient.Repository.Content.GetAllContentsByRef(repo.Id, badgeFilePath, branchRef.Ref); var badgeFile = contents!.First(); @@ -97,6 +105,7 @@ public async Task GetBadgeAsync(string badgeUrl) private static string GetRelativeFolderPath(string url) { + // ToDo: Remove src dependence. var urlSrcIndex = url.IndexOf("src", StringComparison.Ordinal); var folderPath = url[urlSrcIndex..]; return folderPath; @@ -108,7 +117,6 @@ private static string GetRelativeFolderPath(string url) var afterTreeSegments = String.Join("", uri.Segments[4..]); foreach (var reference in refs) { - var branchInRefWithEndingSlash = $"{Regex.Replace(reference.Ref, @"^[^/]+/[^/]+/", "")}/"; if (afterTreeSegments.StartsWith(branchInRefWithEndingSlash)) { @@ -125,6 +133,7 @@ private static string GetLastSegmentFromUrl(string url, out string parentFolderP var lastSegment = uri.Segments.Last().TrimEnd('/'); var parentFolderUrl = uri.GetLeftPart(UriPartial.Authority) + string.Join("", uri.Segments.Take(uri.Segments.Length - 1)); + // ToDo: Remove src dependence. var urlSrcIndex = parentFolderUrl.IndexOf("src", StringComparison.Ordinal); parentFolderPath = parentFolderUrl[urlSrcIndex..]; diff --git a/src/CrystallineSociety/Shared/Shared/Services/Contracts/IGitHubBadgeService.cs b/src/CrystallineSociety/Shared/Shared/Services/Contracts/IGitHubBadgeService.cs index df324efe..1cede75f 100644 --- a/src/CrystallineSociety/Shared/Shared/Services/Contracts/IGitHubBadgeService.cs +++ b/src/CrystallineSociety/Shared/Shared/Services/Contracts/IGitHubBadgeService.cs @@ -26,5 +26,10 @@ public interface IGitHubBadgeService /// If the given badgeUrl contains no valid badge file. /// If the badge file content is null. Task GetBadgeAsync(string badgeUrl); + + Task> GetLightBadgesAsync(string url); + + Task GetBadgeAsync(long repositoryId, string sha); + } } From f1f0f843f9fd99577add92f1b4782d574f451603 Mon Sep 17 00:00:00 2001 From: BSeyfi <42473071+BSeyfi@users.noreply.github.com> Date: Wed, 5 Apr 2023 19:39:19 +0330 Subject: [PATCH 25/53] Refactor GetBadgeAsync --- .../GitHubBadgeServiceTests.cs | 14 +--- .../IServiceCollectionExtensions.cs | 5 +- .../Implementations/GitHubBadgeService.cs | 65 +++++++++---------- 3 files changed, 37 insertions(+), 47 deletions(-) diff --git a/src/CrystallineSociety/CrystallineSociety.Server.Api.Test/GitHubBadgeServiceTests.cs b/src/CrystallineSociety/CrystallineSociety.Server.Api.Test/GitHubBadgeServiceTests.cs index eb394a64..4adff6e6 100644 --- a/src/CrystallineSociety/CrystallineSociety.Server.Api.Test/GitHubBadgeServiceTests.cs +++ b/src/CrystallineSociety/CrystallineSociety.Server.Api.Test/GitHubBadgeServiceTests.cs @@ -1,8 +1,6 @@ using CrystallineSociety.Shared.Services.Implementations.BadgeSystem; - using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Hosting; - using Octokit; namespace CrystallineSociety.Server.Api.Test @@ -26,12 +24,6 @@ public void TestInitialize() ).Build(); var configurationService = _testHost.Services.GetRequiredService(); - - TestOrgName = configurationService.GetSection("OrganizationName").Value; - - TestRepoName = configurationService.GetSection("RepositoryName").Value; - - TestBranchName = configurationService.GetSection("BranchName").Value; } [TestMethod] @@ -39,12 +31,12 @@ public async Task GitHubBadge_LoadSimple() { var githubService = _testHost.Services.GetRequiredService(); - var badgeUrl = - "https://github.com/hootanht/cs-system/tree/feature/initial-get-badge-system/src/CrystallineSociety/Shared/CrystallineSociety.Shared.Test/BadgeSystem/SampleBadgeDocs/serialization-badge-sample"; + var badgeUrl = + "https://github.com/hootanht/cs-system/blob/feature/initial-get-badge-system/src/CrystallineSociety/Shared/CrystallineSociety.Shared.Test/BadgeSystem/SampleBadgeDocs/serialization-badge-sample/spec-badge.json"; + //"https://github.com/hootanht/cs-system/blob/feature/initial-get-badge-system/src/CrystallineSociety/Shared/CrystallineSociety.Shared.Test/BadgeSystem/SampleBadgeDocs/serialization-badge-sample"; var badge = await githubService.GetBadgeAsync(badgeUrl); Assert.IsNotNull(badge); - } [TestMethod] diff --git a/src/CrystallineSociety/Server/Api/Extensions/IServiceCollectionExtensions.cs b/src/CrystallineSociety/Server/Api/Extensions/IServiceCollectionExtensions.cs index b929c1fb..7bbdc4b3 100644 --- a/src/CrystallineSociety/Server/Api/Extensions/IServiceCollectionExtensions.cs +++ b/src/CrystallineSociety/Server/Api/Extensions/IServiceCollectionExtensions.cs @@ -9,7 +9,8 @@ using CrystallineSociety.Server.Api.AppHooks; using CrystallineSociety.Server.Api.Models.Account; using CrystallineSociety.Server.Api.Services.Implementations; -using Octokit; +//using Octokit; + namespace Microsoft.Extensions.DependencyInjection; @@ -21,7 +22,7 @@ public static void AddServerServices(this IServiceCollection services) services.AddAppHook(); services.AddTransient(); // ToDo: Complete. - services.AddTransient(); + // services.AddTransient(); } public static void AddIdentity(this IServiceCollection services, IConfiguration configuration) diff --git a/src/CrystallineSociety/Server/Api/Services/Implementations/GitHubBadgeService.cs b/src/CrystallineSociety/Server/Api/Services/Implementations/GitHubBadgeService.cs index 7a287bfa..f3ca72e6 100644 --- a/src/CrystallineSociety/Server/Api/Services/Implementations/GitHubBadgeService.cs +++ b/src/CrystallineSociety/Server/Api/Services/Implementations/GitHubBadgeService.cs @@ -9,7 +9,9 @@ namespace CrystallineSociety.Server.Api.Services.Implementations public partial class GitHubBadgeService : IGitHubBadgeService { [AutoInject] public IBadgeUtilService BadgeUtilService { get; set; } - [AutoInject] public GitHubClient GitHubClient { get; set; } + + //todo:REMOVE [AutoInject] public GitHubClient GitHubClient { get; set; } + public GitHubClient GitHubClient { get; set; } = CreateClient(); public async Task> GetBadgesAsync(string folderUrl) { @@ -24,9 +26,9 @@ public async Task> GetBadgesAsync(string folderUrl) continue; var badgeDto = await GetBadgeAsync( - lightBadge.RepoId ?? throw new Exception("RepoId of light badge is null."), + lightBadge.RepoId ?? throw new Exception("RepoId of light badge is null."), lightBadge.Sha ?? throw new Exception("Sha of light badge is null.") - ); + ); badges.Add(badgeDto); } @@ -49,48 +51,29 @@ public async Task> GetLightBadgesAsync(string url) var allContents = await GitHubClient.Git.Tree.GetRecursive(repositoryId, folderSha); return allContents.Tree - .Select(t => new BadgeDto { RepoId = repositoryId, Sha = t.Sha, Url = t.Url }) - .ToList(); - } - - public async Task GetBadgeAsync(long repositoryId, string sha) - { - var badgeBlob = await GitHubClient.Git.Blob.Get(repositoryId, sha); - - var bytes = Convert.FromBase64String(badgeBlob.Content); - var badgeContent = Encoding.UTF8.GetString(bytes); - var badgeDto = BadgeUtilService.ParseBadge(badgeContent); - return badgeDto; + .Select(t => new BadgeDto {RepoId = repositoryId, Sha = t.Sha, Url = t.Url}) + .ToList(); } - - private static GitHubClient CreateClient() - { - return new GitHubClient(new ProductHeaderValue("CS-System")); - } - + public async Task GetBadgeAsync(string badgeUrl) { var (orgName, repoName) = GetRepoAndOrgNameFromUrl(badgeUrl); var repo = await GitHubClient.Repository.Get(orgName, repoName); - var refs = await GitHubClient.Git.Reference.GetAll(repo.Id); var branchName = GetBranchNameFromUrl(badgeUrl, refs) ?? throw new ResourceNotFoundException($"Unable to locate branchName: {badgeUrl}"); - + var branchRef = refs.First(r => r.Ref.Contains($"refs/heads/{branchName}")); - var folderPath = GetRelativeFolderPath(badgeUrl); - var folderContents = - await GitHubClient.Repository.Content.GetAllContentsByRef(repo.Id, folderPath, branchRef.Ref); - - var badgeFilePath = - folderContents.FirstOrDefault(x => x.Name.EndsWith("-badge.json"))?.Path - ?? throw new FileNotFoundException($"Badge file not found in: {badgeUrl}"); + var badgeFilePath = GetRelativePath(badgeUrl.EndsWith("-badge.json") + ? badgeUrl + : throw new FileNotFoundException($"Badge file not found in: {badgeUrl}")); - var contents = await GitHubClient.Repository.Content.GetAllContentsByRef(repo.Id, badgeFilePath, branchRef.Ref); + //ToDo: Check is there any method to get single file content? + var contents = + await GitHubClient.Repository.Content.GetAllContentsByRef(repo.Id, badgeFilePath, branchRef.Ref); var badgeFile = contents!.First(); - var badgeFileContent = badgeFile.Content; - + try { var badge = BadgeUtilService.ParseBadge(badgeFileContent); @@ -100,10 +83,24 @@ public async Task GetBadgeAsync(string badgeUrl) { throw new FormatException($"Can not parse badge with badgeUrl: '{badgeUrl}'", exception); } + } + + public async Task GetBadgeAsync(long repositoryId, string sha) + { + var badgeBlob = await GitHubClient.Git.Blob.Get(repositoryId, sha); + var bytes = Convert.FromBase64String(badgeBlob.Content); + var badgeContent = Encoding.UTF8.GetString(bytes); + var badgeDto = BadgeUtilService.ParseBadge(badgeContent); + return badgeDto; + } + + private static GitHubClient CreateClient() + { + return new GitHubClient(new ProductHeaderValue("CS-System")); } - private static string GetRelativeFolderPath(string url) + private static string GetRelativePath(string url) { // ToDo: Remove src dependence. var urlSrcIndex = url.IndexOf("src", StringComparison.Ordinal); From d20f211257a4e4dc00081c9d12519e3b30cc42c9 Mon Sep 17 00:00:00 2001 From: BSeyfi <42473071+BSeyfi@users.noreply.github.com> Date: Wed, 5 Apr 2023 20:28:30 +0330 Subject: [PATCH 26/53] Complete Test Methods --- .../GitHubBadgeServiceTests.cs | 46 +++++++++++++------ 1 file changed, 33 insertions(+), 13 deletions(-) diff --git a/src/CrystallineSociety/CrystallineSociety.Server.Api.Test/GitHubBadgeServiceTests.cs b/src/CrystallineSociety/CrystallineSociety.Server.Api.Test/GitHubBadgeServiceTests.cs index 4adff6e6..47d66573 100644 --- a/src/CrystallineSociety/CrystallineSociety.Server.Api.Test/GitHubBadgeServiceTests.cs +++ b/src/CrystallineSociety/CrystallineSociety.Server.Api.Test/GitHubBadgeServiceTests.cs @@ -32,29 +32,31 @@ public async Task GitHubBadge_LoadSimple() var githubService = _testHost.Services.GetRequiredService(); var badgeUrl = - "https://github.com/hootanht/cs-system/blob/feature/initial-get-badge-system/src/CrystallineSociety/Shared/CrystallineSociety.Shared.Test/BadgeSystem/SampleBadgeDocs/serialization-badge-sample/spec-badge.json"; - //"https://github.com/hootanht/cs-system/blob/feature/initial-get-badge-system/src/CrystallineSociety/Shared/CrystallineSociety.Shared.Test/BadgeSystem/SampleBadgeDocs/serialization-badge-sample"; + "https://github.com/hootanht/cs-system/blob/feature/initial-get-badge-system/src/CrystallineSociety/Shared/CrystallineSociety.Shared.Test/BadgeSystem/SampleBadgeDocs/serialization-badge-sample/spec-badge.json"; + var badge = await githubService.GetBadgeAsync(badgeUrl); Assert.IsNotNull(badge); } - + [TestMethod] - public async Task GitHubBadgesList_LoadSimple() + public async Task GitHubBadge_LoadByFolderAddress_FileNotFoundException() { var githubService = _testHost.Services.GetRequiredService(); - var factory = _testHost.Services.GetRequiredService(); - var badgesFolderUrl = - "https://github.com/cs-internship/cs-system/tree/feature/initial-get-badge-system/src/CrystallineSociety/Shared/CrystallineSociety.Shared.Test/BadgeSystem/SampleBadgeDocs/github-sample-folder"; - var badges = await githubService.GetBadgesAsync(badgesFolderUrl); + var badgeUrl = + "https://github.com/hootanht/cs-system/blob/feature/initial-get-badge-system/src/CrystallineSociety/Shared/CrystallineSociety.Shared.Test/BadgeSystem/SampleBadgeDocs/serialization-badge-sample"; - Assert.IsNotNull(badges); - Assert.AreEqual(6, badges.Count); - var badgeSystem = factory.CreateNew(badges); - } + var exception = await Assert.ThrowsExceptionAsync(async () => + { + await githubService.GetBadgeAsync(badgeUrl); + }); + Assert.IsNotNull(exception); + Assert.AreEqual($"Badge file not found in: {badgeUrl}", exception.Message); + } + [TestMethod] public async Task GetBadgeAsync_IncorrectBranchName_ThrowResourceNotFoundException() { @@ -120,7 +122,25 @@ public async Task GetBadgeAsync_UnparsableBadgeFileFormat_ThrowFormatException() }); Assert.IsNotNull(exception); - Assert.AreEqual($"Can not parse badge with url: '{badgeUrl}'", exception.Message); + Assert.AreEqual($"Can not parse badge with badgeUrl: '{badgeUrl}'", exception.Message); + } + + [TestMethod] + public async Task GitHubBadgesList_LoadSimple() + { + var githubService = _testHost.Services.GetRequiredService(); + var factory = _testHost.Services.GetRequiredService(); + + var badgesFolderUrl = + "https://github.com/cs-internship/cs-system/tree/feature/initial-get-badge-system/src/CrystallineSociety/Shared/CrystallineSociety.Shared.Test/BadgeSystem/SampleBadgeDocs/github-sample-folder"; + var badges = await githubService.GetBadgesAsync(badgesFolderUrl); + + Assert.IsNotNull(badges); + Assert.AreEqual(6, badges.Count); + + var badgeSystem = factory.CreateNew(badges); } + + } } \ No newline at end of file From f70c45fc29f877da85b990e4f250e2caf285a8fe Mon Sep 17 00:00:00 2001 From: Hootan HT Date: Wed, 5 Apr 2023 21:29:07 +0330 Subject: [PATCH 27/53] fix injection of GitHub client. --- .../GitHubBadgeServiceTests.cs | 75 +++++++++++++------ .../IServiceCollectionExtensions.cs | 11 ++- .../Implementations/GitHubBadgeService.cs | 68 +++++++++-------- 3 files changed, 100 insertions(+), 54 deletions(-) diff --git a/src/CrystallineSociety/CrystallineSociety.Server.Api.Test/GitHubBadgeServiceTests.cs b/src/CrystallineSociety/CrystallineSociety.Server.Api.Test/GitHubBadgeServiceTests.cs index 4adff6e6..e7e0de44 100644 --- a/src/CrystallineSociety/CrystallineSociety.Server.Api.Test/GitHubBadgeServiceTests.cs +++ b/src/CrystallineSociety/CrystallineSociety.Server.Api.Test/GitHubBadgeServiceTests.cs @@ -1,6 +1,8 @@ using CrystallineSociety.Shared.Services.Implementations.BadgeSystem; + using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Hosting; + using Octokit; namespace CrystallineSociety.Server.Api.Test @@ -10,12 +12,10 @@ public class GitHubBadgeServiceTests { public TestContext TestContext { get; set; } = default!; - private IHost _testHost = default!; - - [TestInitialize] - public void TestInitialize() + [TestMethod] + public async Task GitHubBadge_LoadSimple() { - _testHost = Host.CreateDefaultBuilder() + var testHost = Host.CreateDefaultBuilder() .ConfigureServices((_, services) => { services.AddSharedServices(); @@ -23,17 +23,11 @@ public void TestInitialize() } ).Build(); - var configurationService = _testHost.Services.GetRequiredService(); - } + var githubService = testHost.Services.GetRequiredService(); - [TestMethod] - public async Task GitHubBadge_LoadSimple() - { - var githubService = _testHost.Services.GetRequiredService(); - - var badgeUrl = + var badgeUrl = "https://github.com/hootanht/cs-system/blob/feature/initial-get-badge-system/src/CrystallineSociety/Shared/CrystallineSociety.Shared.Test/BadgeSystem/SampleBadgeDocs/serialization-badge-sample/spec-badge.json"; - //"https://github.com/hootanht/cs-system/blob/feature/initial-get-badge-system/src/CrystallineSociety/Shared/CrystallineSociety.Shared.Test/BadgeSystem/SampleBadgeDocs/serialization-badge-sample"; + //"https://github.com/hootanht/cs-system/blob/feature/initial-get-badge-system/src/CrystallineSociety/Shared/CrystallineSociety.Shared.Test/BadgeSystem/SampleBadgeDocs/serialization-badge-sample"; var badge = await githubService.GetBadgeAsync(badgeUrl); Assert.IsNotNull(badge); @@ -42,11 +36,19 @@ public async Task GitHubBadge_LoadSimple() [TestMethod] public async Task GitHubBadgesList_LoadSimple() { - var githubService = _testHost.Services.GetRequiredService(); - var factory = _testHost.Services.GetRequiredService(); + var testHost = Host.CreateDefaultBuilder() + .ConfigureServices((_, services) => + { + services.AddSharedServices(); + services.AddServerServices(); + } + ).Build(); + + var githubService = testHost.Services.GetRequiredService(); + var factory = testHost.Services.GetRequiredService(); var badgesFolderUrl = - "https://github.com/cs-internship/cs-system/tree/feature/initial-get-badge-system/src/CrystallineSociety/Shared/CrystallineSociety.Shared.Test/BadgeSystem/SampleBadgeDocs/github-sample-folder"; + "https://github.com/cs-internship/cs-system/tree/main/src/CrystallineSociety/Shared/CrystallineSociety.Shared.Test/BadgeSystem/SampleBadgeDocs/github-sample-folder"; var badges = await githubService.GetBadgesAsync(badgesFolderUrl); Assert.IsNotNull(badges); @@ -58,7 +60,14 @@ public async Task GitHubBadgesList_LoadSimple() [TestMethod] public async Task GetBadgeAsync_IncorrectBranchName_ThrowResourceNotFoundException() { - var githubService = _testHost.Services.GetRequiredService(); + var testHost = Host.CreateDefaultBuilder() + .ConfigureServices((_, services) => + { + services.AddSharedServices(); + services.AddServerServices(); + } + ).Build(); + var githubService = testHost.Services.GetRequiredService(); var badgeUrl = "https://github.com/hootanht/cs-system/tree/feature-initial-get-badge-system/src/CrystallineSociety/Shared/CrystallineSociety.Shared.Test/BadgeSystem/SampleBadgeDocs/serialization-badge-sample"; @@ -75,7 +84,15 @@ public async Task GetBadgeAsync_IncorrectBranchName_ThrowResourceNotFoundExcepti [TestMethod] public async Task GetBadgeAsync_IncorrectOrganizationOrRepositoryName_ThrowOctokitNotFoundException() { - var githubService = _testHost.Services.GetRequiredService(); + var testHost = Host.CreateDefaultBuilder() + .ConfigureServices((_, services) => + { + services.AddSharedServices(); + services.AddServerServices(); + } + ).Build(); + + var githubService = testHost.Services.GetRequiredService(); var badgeUrl = "https://github.com/hootanhtbug/cs-system/tree/feature/initial-get-badge-system/src/CrystallineSociety/Shared/CrystallineSociety.Shared.Test/BadgeSystem/SampleBadgeDocs/serialization-badge-sample"; @@ -92,7 +109,15 @@ public async Task GetBadgeAsync_IncorrectOrganizationOrRepositoryName_ThrowOctok [TestMethod] public async Task GetBadgeAsync_InvalidBadgeFileName_ThrowFileNotFoundException() { - var githubService = _testHost.Services.GetRequiredService(); + var testHost = Host.CreateDefaultBuilder() + .ConfigureServices((_, services) => + { + services.AddSharedServices(); + services.AddServerServices(); + } + ).Build(); + + var githubService = testHost.Services.GetRequiredService(); var badgeUrl = "https://github.com/hootanht/cs-system/tree/feature/initial-get-badge-system/src/CrystallineSociety/Shared/CrystallineSociety.Shared.Test/BadgeSystem/SampleBadgeDocs/serialization-badge-sample/spec.json"; @@ -109,7 +134,15 @@ public async Task GetBadgeAsync_InvalidBadgeFileName_ThrowFileNotFoundException( [TestMethod] public async Task GetBadgeAsync_UnparsableBadgeFileFormat_ThrowFormatException() { - var githubService = _testHost.Services.GetRequiredService(); + var testHost = Host.CreateDefaultBuilder() + .ConfigureServices((_, services) => + { + services.AddSharedServices(); + services.AddServerServices(); + } + ).Build(); + + var githubService = testHost.Services.GetRequiredService(); var badgeUrl = "https://github.com/hootanht/cs-system/tree/feature/initial-get-badge-system/src/CrystallineSociety/Shared/CrystallineSociety.Shared.Test/BadgeSystem/SampleBadgeDocs/serialization-badge-sample/spec-empty-badge.json"; diff --git a/src/CrystallineSociety/Server/Api/Extensions/IServiceCollectionExtensions.cs b/src/CrystallineSociety/Server/Api/Extensions/IServiceCollectionExtensions.cs index 7bbdc4b3..3652552d 100644 --- a/src/CrystallineSociety/Server/Api/Extensions/IServiceCollectionExtensions.cs +++ b/src/CrystallineSociety/Server/Api/Extensions/IServiceCollectionExtensions.cs @@ -9,7 +9,8 @@ using CrystallineSociety.Server.Api.AppHooks; using CrystallineSociety.Server.Api.Models.Account; using CrystallineSociety.Server.Api.Services.Implementations; -//using Octokit; +using Octokit; +using User = CrystallineSociety.Server.Api.Models.Account.User; namespace Microsoft.Extensions.DependencyInjection; @@ -22,7 +23,7 @@ public static void AddServerServices(this IServiceCollection services) services.AddAppHook(); services.AddTransient(); // ToDo: Complete. - // services.AddTransient(); + services.AddTransient(CreateGitHubClient); } public static void AddIdentity(this IServiceCollection services, IConfiguration configuration) @@ -180,4 +181,10 @@ public static void AddHealthChecks(this IServiceCollection services, IWebHostEnv }); } } + + private static GitHubClient CreateGitHubClient(IServiceProvider serviceProvider) + { + var productHeaderValue = new ProductHeaderValue("CS-System"); + return new GitHubClient(productHeaderValue); + } } diff --git a/src/CrystallineSociety/Server/Api/Services/Implementations/GitHubBadgeService.cs b/src/CrystallineSociety/Server/Api/Services/Implementations/GitHubBadgeService.cs index f3ca72e6..6d58af2e 100644 --- a/src/CrystallineSociety/Server/Api/Services/Implementations/GitHubBadgeService.cs +++ b/src/CrystallineSociety/Server/Api/Services/Implementations/GitHubBadgeService.cs @@ -1,7 +1,9 @@ using System.Reflection.Metadata; using System.Text; using System.Text.RegularExpressions; + using CrystallineSociety.Shared.Dtos.BadgeSystem; + using Octokit; namespace CrystallineSociety.Server.Api.Services.Implementations @@ -10,8 +12,7 @@ public partial class GitHubBadgeService : IGitHubBadgeService { [AutoInject] public IBadgeUtilService BadgeUtilService { get; set; } - //todo:REMOVE [AutoInject] public GitHubClient GitHubClient { get; set; } - public GitHubClient GitHubClient { get; set; } = CreateClient(); + [AutoInject] public GitHubClient GitHubClient { get; set; } public async Task> GetBadgesAsync(string folderUrl) { @@ -22,7 +23,7 @@ public async Task> GetBadgesAsync(string folderUrl) // ToDo: Ask about throwing an exception for any problematic item or just pass to the next item? foreach (var lightBadge in lightBadges) { - if (lightBadge.Url is null || !lightBadge.Url.EndsWith("-badge.json")) + if (lightBadge.Url is null) continue; var badgeDto = await GetBadgeAsync( @@ -39,10 +40,10 @@ public async Task> GetBadgesAsync(string folderUrl) public async Task> GetLightBadgesAsync(string url) { var (orgName, repoName) = GetRepoAndOrgNameFromUrl(url); - var repos = await GitHubClient.Repository.GetAllForOrg(orgName); - var repo = repos.First(r => r.Name == repoName); + var repo = await GitHubClient.Repository.Get(orgName, repoName); + var refs = await GitHubClient.Git.Reference.GetAll(repo.Id); - var lastSegment = GetLastSegmentFromUrl(url, out var parentFolderPath); + var lastSegment = GetLastSegmentFromUrl(url,refs,out var parentFolderPath); var repositoryId = repo.Id; // ToDo: Handle if the given url is a file url instead of a folder url which is required here. @@ -51,10 +52,11 @@ public async Task> GetLightBadgesAsync(string url) var allContents = await GitHubClient.Git.Tree.GetRecursive(repositoryId, folderSha); return allContents.Tree - .Select(t => new BadgeDto {RepoId = repositoryId, Sha = t.Sha, Url = t.Url}) + .Where(t=>t.Type == TreeType.Blob ) + .Select(t => new BadgeDto { RepoId = repositoryId, Sha = t.Sha, Url = t.Url }) .ToList(); } - + public async Task GetBadgeAsync(string badgeUrl) { var (orgName, repoName) = GetRepoAndOrgNameFromUrl(badgeUrl); @@ -62,17 +64,17 @@ public async Task GetBadgeAsync(string badgeUrl) var refs = await GitHubClient.Git.Reference.GetAll(repo.Id); var branchName = GetBranchNameFromUrl(badgeUrl, refs) ?? throw new ResourceNotFoundException($"Unable to locate branchName: {badgeUrl}"); - + var branchRef = refs.First(r => r.Ref.Contains($"refs/heads/{branchName}")); var badgeFilePath = GetRelativePath(badgeUrl.EndsWith("-badge.json") ? badgeUrl - : throw new FileNotFoundException($"Badge file not found in: {badgeUrl}")); + : throw new FileNotFoundException($"Badge file not found in: {badgeUrl}"), refs); //ToDo: Check is there any method to get single file content? - var contents = - await GitHubClient.Repository.Content.GetAllContentsByRef(repo.Id, badgeFilePath, branchRef.Ref); - var badgeFile = contents!.First(); - var badgeFileContent = badgeFile.Content; + var badgeFileContentByte = + await GitHubClient.Repository.Content.GetRawContentByRef(orgName, repoName, badgeFilePath, branchRef.Ref); + + var badgeFileContent = Encoding.UTF8.GetString(badgeFileContentByte); try { @@ -84,7 +86,7 @@ public async Task GetBadgeAsync(string badgeUrl) throw new FormatException($"Can not parse badge with badgeUrl: '{badgeUrl}'", exception); } } - + public async Task GetBadgeAsync(long repositoryId, string sha) { var badgeBlob = await GitHubClient.Git.Blob.Get(repositoryId, sha); @@ -94,24 +96,29 @@ public async Task GetBadgeAsync(long repositoryId, string sha) var badgeDto = BadgeUtilService.ParseBadge(badgeContent); return badgeDto; } - - private static GitHubClient CreateClient() - { - return new GitHubClient(new ProductHeaderValue("CS-System")); - } - private static string GetRelativePath(string url) + private static string? GetRelativePath(string url, IEnumerable refs) { - // ToDo: Remove src dependence. - var urlSrcIndex = url.IndexOf("src", StringComparison.Ordinal); - var folderPath = url[urlSrcIndex..]; - return folderPath; + var uri = new Uri(url); + var afterTreeSegments = string.Join("", uri.Segments[4..]); + foreach (var reference in refs) + { + var branchInRefWithEndingSlash = $"{Regex.Replace(reference.Ref, @"^[^/]+/[^/]+/", "")}/"; + + if (!afterTreeSegments.StartsWith(branchInRefWithEndingSlash)) + continue; + + var path = afterTreeSegments.Replace(branchInRefWithEndingSlash, string.Empty).TrimEnd('/'); + return path; + } + + return null; } - private static string? GetBranchNameFromUrl(string url, IReadOnlyList refs) + private static string? GetBranchNameFromUrl(string url, IEnumerable refs) { var uri = new Uri(url); - var afterTreeSegments = String.Join("", uri.Segments[4..]); + var afterTreeSegments = string.Join("", uri.Segments[4..]); foreach (var reference in refs) { var branchInRefWithEndingSlash = $"{Regex.Replace(reference.Ref, @"^[^/]+/[^/]+/", "")}/"; @@ -124,15 +131,14 @@ private static string GetRelativePath(string url) return null; } - private static string GetLastSegmentFromUrl(string url, out string parentFolderPath) + private static string GetLastSegmentFromUrl(string url, IEnumerable refs, out string parentFolderPath) { var uri = new Uri(url); var lastSegment = uri.Segments.Last().TrimEnd('/'); var parentFolderUrl = uri.GetLeftPart(UriPartial.Authority) + string.Join("", uri.Segments.Take(uri.Segments.Length - 1)); - // ToDo: Remove src dependence. - var urlSrcIndex = parentFolderUrl.IndexOf("src", StringComparison.Ordinal); - parentFolderPath = parentFolderUrl[urlSrcIndex..]; + // ToDo: write better message for here + parentFolderPath = GetRelativePath(parentFolderUrl, refs) ?? throw new InvalidOperationException("invalid operation"); return lastSegment; } From 92efe59ca1432aba6f71225761e165b3a41b0437 Mon Sep 17 00:00:00 2001 From: Hootan HT Date: Thu, 6 Apr 2023 17:29:51 +0330 Subject: [PATCH 28/53] Remove to dos we don't need that anymore. --- .../Services/Implementations/GitHubBadgeService.cs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/CrystallineSociety/Server/Api/Services/Implementations/GitHubBadgeService.cs b/src/CrystallineSociety/Server/Api/Services/Implementations/GitHubBadgeService.cs index 6d58af2e..37a82a75 100644 --- a/src/CrystallineSociety/Server/Api/Services/Implementations/GitHubBadgeService.cs +++ b/src/CrystallineSociety/Server/Api/Services/Implementations/GitHubBadgeService.cs @@ -19,8 +19,7 @@ public async Task> GetBadgesAsync(string folderUrl) var lightBadges = await GetLightBadgesAsync(folderUrl); var badges = new List(); - - // ToDo: Ask about throwing an exception for any problematic item or just pass to the next item? + foreach (var lightBadge in lightBadges) { if (lightBadge.Url is null) @@ -69,8 +68,7 @@ public async Task GetBadgeAsync(string badgeUrl) var badgeFilePath = GetRelativePath(badgeUrl.EndsWith("-badge.json") ? badgeUrl : throw new FileNotFoundException($"Badge file not found in: {badgeUrl}"), refs); - - //ToDo: Check is there any method to get single file content? + var badgeFileContentByte = await GitHubClient.Repository.Content.GetRawContentByRef(orgName, repoName, badgeFilePath, branchRef.Ref); @@ -131,14 +129,14 @@ public async Task GetBadgeAsync(long repositoryId, string sha) return null; } - private static string GetLastSegmentFromUrl(string url, IEnumerable refs, out string parentFolderPath) + private static string GetLastSegmentFromUrl(string url, IEnumerable refs, out string? parentFolderPath) { var uri = new Uri(url); var lastSegment = uri.Segments.Last().TrimEnd('/'); var parentFolderUrl = uri.GetLeftPart(UriPartial.Authority) + string.Join("", uri.Segments.Take(uri.Segments.Length - 1)); - // ToDo: write better message for here - parentFolderPath = GetRelativePath(parentFolderUrl, refs) ?? throw new InvalidOperationException("invalid operation"); + + parentFolderPath = GetRelativePath(parentFolderUrl, refs); return lastSegment; } From 4e5e52b52f8789d64989b3f8b9f899a458fc0c1a Mon Sep 17 00:00:00 2001 From: BSeyfi <42473071+BSeyfi@users.noreply.github.com> Date: Sat, 8 Apr 2023 20:49:50 +0330 Subject: [PATCH 29/53] Complete GetBadgeAsync Tests --- .../GitHubBadgeServiceTests.cs | 68 +++++++++++++------ 1 file changed, 48 insertions(+), 20 deletions(-) diff --git a/src/CrystallineSociety/CrystallineSociety.Server.Api.Test/GitHubBadgeServiceTests.cs b/src/CrystallineSociety/CrystallineSociety.Server.Api.Test/GitHubBadgeServiceTests.cs index aa99c517..3f7c6f66 100644 --- a/src/CrystallineSociety/CrystallineSociety.Server.Api.Test/GitHubBadgeServiceTests.cs +++ b/src/CrystallineSociety/CrystallineSociety.Server.Api.Test/GitHubBadgeServiceTests.cs @@ -1,8 +1,6 @@ using CrystallineSociety.Shared.Services.Implementations.BadgeSystem; - using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Hosting; - using Octokit; namespace CrystallineSociety.Server.Api.Test @@ -27,7 +25,7 @@ public async Task GitHubBadge_LoadSimple() var githubService = testHost.Services.GetRequiredService(); var badgeUrl = - "https://github.com/hootanht/cs-system/blob/feature/initial-get-badge-system/src/CrystallineSociety/Shared/CrystallineSociety.Shared.Test/BadgeSystem/SampleBadgeDocs/serialization-badge-sample/spec-badge.json"; + "https://github.com/hootanht/cs-system/blob/feature/initial-get-badge-system/src/CrystallineSociety/Shared/CrystallineSociety.Shared.Test/BadgeSystem/SampleBadgeDocs/serialization-badge-sample/spec-badge.json"; var badge = await githubService.GetBadgeAsync(badgeUrl); @@ -66,10 +64,10 @@ public async Task GetBadgeAsync_IncorrectBranchName_ThrowResourceNotFoundExcepti { var testHost = Host.CreateDefaultBuilder() .ConfigureServices((_, services) => - { - services.AddSharedServices(); - services.AddServerServices(); - } + { + services.AddSharedServices(); + services.AddServerServices(); + } ).Build(); var githubService = testHost.Services.GetRequiredService(); @@ -81,9 +79,6 @@ public async Task GetBadgeAsync_IncorrectBranchName_ThrowResourceNotFoundExcepti { await githubService.GetBadgeAsync(badgeUrl); }); - - Assert.IsNotNull(ex); - Assert.AreEqual($"Unable to locate branchName: {badgeUrl}", ex.Message); } [TestMethod] @@ -106,9 +101,6 @@ public async Task GetBadgeAsync_IncorrectOrganizationOrRepositoryName_ThrowOctok { await githubService.GetBadgeAsync(badgeUrl); }); - - Assert.IsNotNull(ex); - Assert.AreEqual($"Not Found", ex.Message); } [TestMethod] @@ -131,9 +123,6 @@ public async Task GetBadgeAsync_InvalidBadgeFileName_ThrowFileNotFoundException( { await githubService.GetBadgeAsync(badgeUrl); }); - - Assert.IsNotNull(exception); - Assert.AreEqual($"Badge file not found in: {badgeUrl}", exception.Message); } [TestMethod] @@ -156,9 +145,50 @@ public async Task GetBadgeAsync_UnparsableBadgeFileFormat_ThrowFormatException() { await githubService.GetBadgeAsync(badgeUrl); }); + } - Assert.IsNotNull(exception); - Assert.AreEqual($"Can not parse badge with badgeUrl: '{badgeUrl}'", exception.Message); + + [TestMethod] + public async Task GetBadgeAsync_InvalidRepoIdOrSha_ThrowNotFoundException() + { + var testHost = Host.CreateDefaultBuilder() + .ConfigureServices((_, services) => + { + services.AddSharedServices(); + services.AddServerServices(); + } + ).Build(); + + var githubService = testHost.Services.GetRequiredService(); + + long repoId = 619299373; //correct: 619299373 + string invalidSha = + "cf74dc41bb1a474fe7ff3e2624aae055ee5338ec"; //correct: cf74dc41bb1a474fe7ff3e2624aae055ee5338eb + + var exception = await Assert.ThrowsExceptionAsync(async () => + { + await githubService.GetBadgeAsync(repoId, invalidSha); + }); + } + + [TestMethod] + public async Task GetBadgeAsync_ValidRepoIdAndSha_AreEqual() + { + var testHost = Host.CreateDefaultBuilder() + .ConfigureServices((_, services) => + { + services.AddSharedServices(); + services.AddServerServices(); + } + ).Build(); + + var githubService = testHost.Services.GetRequiredService(); + + long repoId = 619299373; + string sha = "cf74dc41bb1a474fe7ff3e2624aae055ee5338eb"; + + var result = await githubService.GetBadgeAsync(repoId, sha); + Assert.AreEqual("doc-beginner", result.Code); } [TestMethod] @@ -184,7 +214,5 @@ public async Task GitHubBadgesList_LoadSimple() var badgeSystem = factory.CreateNew(badges); } - - } } \ No newline at end of file From 96e485615fd562f9b7a37f6852289fa3eb9f052e Mon Sep 17 00:00:00 2001 From: Hootan HT Date: Sun, 9 Apr 2023 13:48:30 +0330 Subject: [PATCH 30/53] Change property declaration. --- .../Shared/Shared/Dtos/BadgeSystem/BadgeBundleDto.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/CrystallineSociety/Shared/Shared/Dtos/BadgeSystem/BadgeBundleDto.cs b/src/CrystallineSociety/Shared/Shared/Dtos/BadgeSystem/BadgeBundleDto.cs index 9cd1253b..7753d3e1 100644 --- a/src/CrystallineSociety/Shared/Shared/Dtos/BadgeSystem/BadgeBundleDto.cs +++ b/src/CrystallineSociety/Shared/Shared/Dtos/BadgeSystem/BadgeBundleDto.cs @@ -8,6 +8,9 @@ namespace CrystallineSociety.Shared.Dtos.BadgeSystem { public class BadgeBundleDto { + public List Badges { get; set; } = new(); + public List? Validations { get; set; } + public BadgeBundleDto() { @@ -18,9 +21,6 @@ public BadgeBundleDto(List badges) Badges = badges; } - public List Badges { get; set; } = new(); - public List? Validations { get; set; } - public bool BadgeExists(string badgeCode) { return Badges.Any(b => b.Code == badgeCode); From 1021893b82dc821fc4d7cb8a88f7ec0dab43be39 Mon Sep 17 00:00:00 2001 From: Hootan HT Date: Sun, 9 Apr 2023 14:43:41 +0330 Subject: [PATCH 31/53] Add BadgeTree Component. --- .../Shared/Components/BadgeSystem.razor | 5 +-- .../Client/Shared/Components/BadgeTree.razor | 9 +++++ .../Shared/Components/BadgeTree.razor.cs | 34 +++++++++++++++++++ .../Shared/Components/BadgeTree.razor.scss | 0 4 files changed, 44 insertions(+), 4 deletions(-) create mode 100644 src/CrystallineSociety/Client/Shared/Components/BadgeTree.razor create mode 100644 src/CrystallineSociety/Client/Shared/Components/BadgeTree.razor.cs create mode 100644 src/CrystallineSociety/Client/Shared/Components/BadgeTree.razor.scss diff --git a/src/CrystallineSociety/Client/Shared/Components/BadgeSystem.razor b/src/CrystallineSociety/Client/Shared/Components/BadgeSystem.razor index fa897fab..e5dafccc 100644 --- a/src/CrystallineSociety/Client/Shared/Components/BadgeSystem.razor +++ b/src/CrystallineSociety/Client/Shared/Components/BadgeSystem.razor @@ -1,6 +1,3 @@ 
- Tada from Badge System +
-
-    @GetBundleText(Bundle)
-
diff --git a/src/CrystallineSociety/Client/Shared/Components/BadgeTree.razor b/src/CrystallineSociety/Client/Shared/Components/BadgeTree.razor new file mode 100644 index 00000000..4b04b554 --- /dev/null +++ b/src/CrystallineSociety/Client/Shared/Components/BadgeTree.razor @@ -0,0 +1,9 @@ +@if (BadgeCodes != null) +{ + @foreach (var badeCode in BadgeCodes) + { +
@badeCode
+ } +} + +test \ No newline at end of file diff --git a/src/CrystallineSociety/Client/Shared/Components/BadgeTree.razor.cs b/src/CrystallineSociety/Client/Shared/Components/BadgeTree.razor.cs new file mode 100644 index 00000000..2eb0f70d --- /dev/null +++ b/src/CrystallineSociety/Client/Shared/Components/BadgeTree.razor.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using CrystallineSociety.Shared.Dtos.BadgeSystem; + +namespace CrystallineSociety.Client.Shared.Components +{ + public partial class BadgeTree + { + [Parameter] public BadgeBundleDto? BadgeBundleDto { get; set; } + + private string?[]? BadgeCodes { get; set; } + + protected override Task OnAfterRenderAsync(bool firstRender) + { + if (BadgeBundleDto != null && firstRender) + { + BadgeCodes = BadgeBundleDto.Badges.Select(b => b.Code).ToArray(); + } + return base.OnAfterRenderAsync(firstRender); + } + + private void Test() + { + if (BadgeBundleDto != null) + { + BadgeCodes = BadgeBundleDto.Badges.Select(b => b.Code).ToArray(); + } + } + } +} diff --git a/src/CrystallineSociety/Client/Shared/Components/BadgeTree.razor.scss b/src/CrystallineSociety/Client/Shared/Components/BadgeTree.razor.scss new file mode 100644 index 00000000..e69de29b From 9fba883b3c9c77df1d2a0de7a1a6b3997e8e71d1 Mon Sep 17 00:00:00 2001 From: Hootan HT Date: Mon, 10 Apr 2023 23:41:43 +0330 Subject: [PATCH 32/53] Add BitBasicList to BadgeTree. --- .../Client/Shared/Components/BadgeTree.razor | 19 ++++++++++++++++--- .../Shared/Components/BadgeTree.razor.cs | 16 ++++------------ .../Shared/Components/BadgeTree.razor.scss | 9 +++++++++ 3 files changed, 29 insertions(+), 15 deletions(-) diff --git a/src/CrystallineSociety/Client/Shared/Components/BadgeTree.razor b/src/CrystallineSociety/Client/Shared/Components/BadgeTree.razor index 4b04b554..e0168627 100644 --- a/src/CrystallineSociety/Client/Shared/Components/BadgeTree.razor +++ b/src/CrystallineSociety/Client/Shared/Components/BadgeTree.razor @@ -1,9 +1,22 @@ -@if (BadgeCodes != null) +@using CrystallineSociety.Shared.Dtos.BadgeSystem +@*@if (BadgeCodes != null) { @foreach (var badeCode in BadgeCodes) {
@badeCode
} -} +}*@ -test \ No newline at end of file +@if (BadgeCodes != null) +{ + + +
+
Name: @badge.Code
+
+
+
+} diff --git a/src/CrystallineSociety/Client/Shared/Components/BadgeTree.razor.cs b/src/CrystallineSociety/Client/Shared/Components/BadgeTree.razor.cs index 2eb0f70d..3b6f6fd9 100644 --- a/src/CrystallineSociety/Client/Shared/Components/BadgeTree.razor.cs +++ b/src/CrystallineSociety/Client/Shared/Components/BadgeTree.razor.cs @@ -12,23 +12,15 @@ public partial class BadgeTree { [Parameter] public BadgeBundleDto? BadgeBundleDto { get; set; } - private string?[]? BadgeCodes { get; set; } + private List? BadgeCodes { get; set; } - protected override Task OnAfterRenderAsync(bool firstRender) - { - if (BadgeBundleDto != null && firstRender) - { - BadgeCodes = BadgeBundleDto.Badges.Select(b => b.Code).ToArray(); - } - return base.OnAfterRenderAsync(firstRender); - } - - private void Test() + protected override Task OnParametersSetAsync() { if (BadgeBundleDto != null) { - BadgeCodes = BadgeBundleDto.Badges.Select(b => b.Code).ToArray(); + BadgeCodes = BadgeBundleDto.Badges.ToList(); } + return base.OnParametersSetAsync(); } } } diff --git a/src/CrystallineSociety/Client/Shared/Components/BadgeTree.razor.scss b/src/CrystallineSociety/Client/Shared/Components/BadgeTree.razor.scss index e69de29b..99ec8953 100644 --- a/src/CrystallineSociety/Client/Shared/Components/BadgeTree.razor.scss +++ b/src/CrystallineSociety/Client/Shared/Components/BadgeTree.razor.scss @@ -0,0 +1,9 @@ +.badge-name-color { + color: white; +} + +.list { + border: 1px #a19f9d solid; + border-radius: 3px; + height: auto; +} \ No newline at end of file From 72acaf06808da40aa867b791dc85969e213c68f8 Mon Sep 17 00:00:00 2001 From: BSeyfi <42473071+BSeyfi@users.noreply.github.com> Date: Tue, 11 Apr 2023 00:12:57 +0330 Subject: [PATCH 33/53] Add GetLightBadgesAsync test methods --- .../GitHubBadgeServiceTests.cs | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/src/CrystallineSociety/CrystallineSociety.Server.Api.Test/GitHubBadgeServiceTests.cs b/src/CrystallineSociety/CrystallineSociety.Server.Api.Test/GitHubBadgeServiceTests.cs index 3f7c6f66..4a086948 100644 --- a/src/CrystallineSociety/CrystallineSociety.Server.Api.Test/GitHubBadgeServiceTests.cs +++ b/src/CrystallineSociety/CrystallineSociety.Server.Api.Test/GitHubBadgeServiceTests.cs @@ -191,6 +191,57 @@ public async Task GetBadgeAsync_ValidRepoIdAndSha_AreEqual() Assert.AreEqual("doc-beginner", result.Code); } + [TestMethod] + public async Task GetLightBadgesAsync_ValidBadgeFileUrl_ThrowInvalidOperationException() + { + var testHost = Host.CreateDefaultBuilder() + .ConfigureServices((_, services) => + { + services.AddSharedServices(); + services.AddServerServices(); + } + ).Build(); + + var githubService = testHost.Services.GetRequiredService(); + var badgesFolderUrl = + "https://github.com/hootanht/cs-system/blob/feature/initial-get-badge-system/src/CrystallineSociety/Shared/CrystallineSociety.Shared.Test/BadgeSystem/SampleBadgeDocs/serialization-badge-sample/spec-badge.json"; + + var exception = await Assert.ThrowsExceptionAsync(async () => + { + await githubService.GetLightBadgesAsync(badgesFolderUrl); + }); + } + + [TestMethod] + public async Task GetLightBadgesAsync_ValidBadgesFolderUrl_AreEqualShaValues() + { + var testHost = Host.CreateDefaultBuilder() + .ConfigureServices((_, services) => + { + services.AddSharedServices(); + services.AddServerServices(); + } + ).Build(); + + var githubService = testHost.Services.GetRequiredService(); + var badgesFolderUrl = + "https://github.com/hootanht/cs-system/tree/feature/initial-get-badge-system/src/CrystallineSociety/Shared/CrystallineSociety.Shared.Test/BadgeSystem/SampleBadgeDocs/github-sample-folder"; + + var result = await githubService.GetLightBadgesAsync(badgesFolderUrl); + Assert.AreEqual(result[0].Sha, + "cf74dc41bb1a474fe7ff3e2624aae055ee5338eb"); + Assert.AreEqual(result[1].Sha, + "7399b9c26213221edf619a9ae5558f7138b970a2"); + Assert.AreEqual(result[2].Sha, + "1c0ba797e86cd01c5ef35673f5951d41de4b69ce"); + Assert.AreEqual(result[3].Sha, + "31b0dabb38f40a631aae96ae4066c828bfd21611"); + Assert.AreEqual(result[4].Sha, + "05ac8473cee780f202ad1f77df18428db5631e85"); + Assert.AreEqual(result[5].Sha, + "ce329c0bcc7307c53b8a695c59e62768c30e84e8"); + } + [TestMethod] public async Task GitHubBadgesList_LoadSimple() { From bcffb9232d44d10f05c6748ef854728697751546 Mon Sep 17 00:00:00 2001 From: Zahra Ahangari Date: Wed, 12 Apr 2023 22:30:44 +0330 Subject: [PATCH 34/53] Add and implement BadgeContent component Co-Authored-By: Hootan HT --- .../Client/Shared/Components/BadgeContent.razor | 7 +++++++ .../Client/Shared/Components/BadgeContent.razor.cs | 14 ++++++++++++++ .../Shared/Components/BadgeContent.razor.scss | 1 + .../Client/Shared/Components/BadgeSystem.razor | 8 ++++++-- .../Client/Shared/Components/BadgeSystem.razor.cs | 11 ++++++----- .../Client/Shared/Components/BadgeTree.razor | 14 ++++---------- .../Client/Shared/Components/BadgeTree.razor.cs | 14 ++++++++++---- 7 files changed, 48 insertions(+), 21 deletions(-) create mode 100644 src/CrystallineSociety/Client/Shared/Components/BadgeContent.razor create mode 100644 src/CrystallineSociety/Client/Shared/Components/BadgeContent.razor.cs create mode 100644 src/CrystallineSociety/Client/Shared/Components/BadgeContent.razor.scss diff --git a/src/CrystallineSociety/Client/Shared/Components/BadgeContent.razor b/src/CrystallineSociety/Client/Shared/Components/BadgeContent.razor new file mode 100644 index 00000000..12f2e0a5 --- /dev/null +++ b/src/CrystallineSociety/Client/Shared/Components/BadgeContent.razor @@ -0,0 +1,7 @@ +@using CrystallineSociety.Shared.Dtos.BadgeSystem +@inherits AppComponentBase + +@Badge?.Description + +

BadgeContent

+ diff --git a/src/CrystallineSociety/Client/Shared/Components/BadgeContent.razor.cs b/src/CrystallineSociety/Client/Shared/Components/BadgeContent.razor.cs new file mode 100644 index 00000000..53b21121 --- /dev/null +++ b/src/CrystallineSociety/Client/Shared/Components/BadgeContent.razor.cs @@ -0,0 +1,14 @@ +using CrystallineSociety.Shared.Dtos.BadgeSystem; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CrystallineSociety.Client.Shared.Components +{ + public partial class BadgeContent + { + [Parameter] public BadgeDto? Badge { get; set; } + } +} diff --git a/src/CrystallineSociety/Client/Shared/Components/BadgeContent.razor.scss b/src/CrystallineSociety/Client/Shared/Components/BadgeContent.razor.scss new file mode 100644 index 00000000..e02abfc9 --- /dev/null +++ b/src/CrystallineSociety/Client/Shared/Components/BadgeContent.razor.scss @@ -0,0 +1 @@ + diff --git a/src/CrystallineSociety/Client/Shared/Components/BadgeSystem.razor b/src/CrystallineSociety/Client/Shared/Components/BadgeSystem.razor index e5dafccc..1c08b7dd 100644 --- a/src/CrystallineSociety/Client/Shared/Components/BadgeSystem.razor +++ b/src/CrystallineSociety/Client/Shared/Components/BadgeSystem.razor @@ -1,3 +1,7 @@ -
- +@using CrystallineSociety.Shared.Dtos.BadgeSystem +@inherits AppComponentBase + +
+ +
diff --git a/src/CrystallineSociety/Client/Shared/Components/BadgeSystem.razor.cs b/src/CrystallineSociety/Client/Shared/Components/BadgeSystem.razor.cs index 8f406408..1ab98806 100644 --- a/src/CrystallineSociety/Client/Shared/Components/BadgeSystem.razor.cs +++ b/src/CrystallineSociety/Client/Shared/Components/BadgeSystem.razor.cs @@ -15,13 +15,9 @@ public partial class BadgeSystem [AutoInject] private IBadgeSystemService BadgeSystemService { get; set; } = default!; [AutoInject] private IBadgeUtilService BadgeUtilService { get; set; } = default!; [Parameter] public BadgeBundleDto? Bundle { get; set; } + private BadgeDto? BadgeDto {get; set;} - protected override Task OnInitializedAsync() - { - return base.OnInitializedAsync(); - } - private string? GetBundleText(BadgeBundleDto bundle) { var builder = new StringBuilder(); @@ -35,5 +31,10 @@ protected override Task OnInitializedAsync() return builder.ToString(); } + + private void GetBadge(BadgeDto badgeDto) + { + BadgeDto = badgeDto; + } } } diff --git a/src/CrystallineSociety/Client/Shared/Components/BadgeTree.razor b/src/CrystallineSociety/Client/Shared/Components/BadgeTree.razor index e0168627..b7670087 100644 --- a/src/CrystallineSociety/Client/Shared/Components/BadgeTree.razor +++ b/src/CrystallineSociety/Client/Shared/Components/BadgeTree.razor @@ -1,21 +1,15 @@ @using CrystallineSociety.Shared.Dtos.BadgeSystem -@*@if (BadgeCodes != null) -{ - @foreach (var badeCode in BadgeCodes) - { -
@badeCode
- } -}*@ +@inherits AppComponentBase -@if (BadgeCodes != null) +@if (Badges != null) {
-
Name: @badge.Code
+
Name: @badge.Code
diff --git a/src/CrystallineSociety/Client/Shared/Components/BadgeTree.razor.cs b/src/CrystallineSociety/Client/Shared/Components/BadgeTree.razor.cs index 3b6f6fd9..9ef42581 100644 --- a/src/CrystallineSociety/Client/Shared/Components/BadgeTree.razor.cs +++ b/src/CrystallineSociety/Client/Shared/Components/BadgeTree.razor.cs @@ -11,16 +11,22 @@ namespace CrystallineSociety.Client.Shared.Components public partial class BadgeTree { [Parameter] public BadgeBundleDto? BadgeBundleDto { get; set; } + [Parameter] public EventCallback BadgeDtoCallBack { get; set; } - private List? BadgeCodes { get; set; } + private List? Badges { get; set; } - protected override Task OnParametersSetAsync() + protected override Task OnParamsSetAsync() { if (BadgeBundleDto != null) { - BadgeCodes = BadgeBundleDto.Badges.ToList(); + Badges = BadgeBundleDto.Badges.ToList(); } - return base.OnParametersSetAsync(); + return base.OnParamsSetAsync(); + } + + private async Task OnBadgeClick(BadgeDto badgeDto) + { + await BadgeDtoCallBack.InvokeAsync(badgeDto); } } } From c303e9d4b7d231703ecfb7feed39c2b9c8ed39a3 Mon Sep 17 00:00:00 2001 From: BSeyfi <42473071+BSeyfi@users.noreply.github.com> Date: Wed, 12 Apr 2023 22:58:28 +0330 Subject: [PATCH 35/53] Add documentation to IGitHubBadgeService and GitHubBadgeService --- .../Implementations/GitHubBadgeService.cs | 30 +++++++++++++- .../Services/Contracts/IGitHubBadgeService.cs | 40 ++++++++++++------- 2 files changed, 55 insertions(+), 15 deletions(-) diff --git a/src/CrystallineSociety/Server/Api/Services/Implementations/GitHubBadgeService.cs b/src/CrystallineSociety/Server/Api/Services/Implementations/GitHubBadgeService.cs index 37a82a75..79d0462d 100644 --- a/src/CrystallineSociety/Server/Api/Services/Implementations/GitHubBadgeService.cs +++ b/src/CrystallineSociety/Server/Api/Services/Implementations/GitHubBadgeService.cs @@ -14,6 +14,14 @@ public partial class GitHubBadgeService : IGitHubBadgeService [AutoInject] public GitHubClient GitHubClient { get; set; } + /// + /// Retrieves objects from badge files located at the GitHub URL provided. + /// This method performs a recursive search for files and selects those whose filename ends with `-badge.json`. + /// + /// GitHub folder URL containing badge files. + /// A list of parsed badge files. + /// When the RepoId of light badge is null. + /// When the Sha of light badge is null. public async Task> GetBadgesAsync(string folderUrl) { var lightBadges = await GetLightBadgesAsync(folderUrl); @@ -24,7 +32,8 @@ public async Task> GetBadgesAsync(string folderUrl) { if (lightBadge.Url is null) continue; - + + //todo Replace Exception with NullReferenceException or another relevant one var badgeDto = await GetBadgeAsync( lightBadge.RepoId ?? throw new Exception("RepoId of light badge is null."), lightBadge.Sha ?? throw new Exception("Sha of light badge is null.") @@ -36,6 +45,11 @@ public async Task> GetBadgesAsync(string folderUrl) return badges; } + /// + /// Recursively loads and parses a lightweight version of badges specifications from a GitHub URL. + /// + /// The GitHub URL pointing to a folder containing badge file(s). All badge files will load recursively. Badge filename must ends with `-badge.json`. + /// A list of lightweight version of s. public async Task> GetLightBadgesAsync(string url) { var (orgName, repoName) = GetRepoAndOrgNameFromUrl(url); @@ -56,6 +70,14 @@ public async Task> GetLightBadgesAsync(string url) .ToList(); } + /// + /// Loads a badge specification from the given and parses it. + /// + /// The badge file GitHub URL. Badge filename must ends with `-badge.json`. + /// The parsed badge object. + /// When unable to locate the GitHub repo branchName. + /// When the given is not a valid badge file URL. + /// When the loaded badge file has an incorrect format and cannot be parsed. public async Task GetBadgeAsync(string badgeUrl) { var (orgName, repoName) = GetRepoAndOrgNameFromUrl(badgeUrl); @@ -85,6 +107,12 @@ public async Task GetBadgeAsync(string badgeUrl) } } + /// + /// Loads and parses a badge specification from a badge file on GitHub identified by the and parameters. + /// + /// The Id of the repository on GtiHub. + /// The SHA Id of the file in the repository on GtiHub. + /// The parsed badge object. public async Task GetBadgeAsync(long repositoryId, string sha) { var badgeBlob = await GitHubClient.Git.Blob.Get(repositoryId, sha); diff --git a/src/CrystallineSociety/Shared/Shared/Services/Contracts/IGitHubBadgeService.cs b/src/CrystallineSociety/Shared/Shared/Services/Contracts/IGitHubBadgeService.cs index 1cede75f..9bc7a914 100644 --- a/src/CrystallineSociety/Shared/Shared/Services/Contracts/IGitHubBadgeService.cs +++ b/src/CrystallineSociety/Shared/Shared/Services/Contracts/IGitHubBadgeService.cs @@ -10,26 +10,38 @@ namespace CrystallineSociety.Shared.Services.Contracts public interface IGitHubBadgeService { /// - /// + /// Retrieves objects from badge files located at the GitHub URL provided. + /// This method performs a recursive search for files and selects those whose filename ends with `-badge.json`. /// - /// - /// + /// GitHub folder URL containing badge files. + /// A list of parsed badge files. + /// When the RepoId of light badge is null. + /// When the Sha of light badge is null. Task> GetBadgesAsync(string folderUrl); - + /// - /// Loads a badge spec from the given and parses it, and returns a BadgeDto. + /// Loads a badge specification from the given and parses it. /// - /// The badgeUrl address that contains badge files or folders containing badge file/folders. - /// Returns the parsed badge. - /// If the loaded badgeUrl string is not in a correct format. - /// If the given badgeUrl could not be found. - /// If the given badgeUrl contains no valid badge file. - /// If the badge file content is null. + /// The badge file GitHub URL. Badge filename must ends with `-badge.json`. + /// The parsed badge object. + /// When unable to locate the GitHub repo branchName. + /// When the given is not a valid badge file URL. + /// When the loaded badge file has an incorrect format and cannot be parsed. Task GetBadgeAsync(string badgeUrl); - Task> GetLightBadgesAsync(string url); - + /// + /// Loads and parses a badge specification from a badge file on GitHub identified by the and parameters. + /// + /// The Id of the repository on GtiHub. + /// The SHA Id of the file in the repository on GtiHub. + /// The parsed badge object. Task GetBadgeAsync(long repositoryId, string sha); + /// + /// Recursively loads and parses a lightweight version of badges specifications from a GitHub URL. + /// + /// The GitHub URL pointing to a folder containing badge file(s). All badge files will load recursively. Badge filename must ends with `-badge.json`. + /// A list of lightweight version of s. + Task> GetLightBadgesAsync(string url); } -} +} \ No newline at end of file From 2b93a00b2f7d28b57e0b1114703a224dc0f17443 Mon Sep 17 00:00:00 2001 From: Vahid Moradpour Date: Thu, 13 Apr 2023 13:16:50 +0330 Subject: [PATCH 36/53] Add GitHubBadgeService private methods doc --- .../Implementations/GitHubBadgeService.cs | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/CrystallineSociety/Server/Api/Services/Implementations/GitHubBadgeService.cs b/src/CrystallineSociety/Server/Api/Services/Implementations/GitHubBadgeService.cs index 79d0462d..ad7dd948 100644 --- a/src/CrystallineSociety/Server/Api/Services/Implementations/GitHubBadgeService.cs +++ b/src/CrystallineSociety/Server/Api/Services/Implementations/GitHubBadgeService.cs @@ -123,6 +123,12 @@ public async Task GetBadgeAsync(long repositoryId, string sha) return badgeDto; } + /// + /// The relative path is created by eliminating the URL segments from the left up to the branch name. Branch name is exclude. + /// + /// A GitHub URL pointing to a file/folder. + /// Octokit references to the GitHub repo. + /// The relative path. private static string? GetRelativePath(string url, IEnumerable refs) { var uri = new Uri(url); @@ -141,6 +147,12 @@ public async Task GetBadgeAsync(long repositoryId, string sha) return null; } + /// + /// Get the branch name from a GitHub URL. + /// + /// A GitHub URL. + /// Octokit references to the GitHub repo. + /// The branch name. private static string? GetBranchNameFromUrl(string url, IEnumerable refs) { var uri = new Uri(url); @@ -157,6 +169,13 @@ public async Task GetBadgeAsync(long repositoryId, string sha) return null; } + /// + /// Extract the last segment of a GitHub URL pointing to a folder. + /// + /// A GitHub URL pointing to a folder. + /// Octokit references to the GitHub repo. + /// Extracted parent folder of the given GitHub URL. + /// The last segment of a GitHub URL. private static string GetLastSegmentFromUrl(string url, IEnumerable refs, out string? parentFolderPath) { var uri = new Uri(url); @@ -169,6 +188,11 @@ private static string GetLastSegmentFromUrl(string url, IEnumerable r return lastSegment; } + /// + /// Retrieves a repository and organization/owner name from GitHub URL. + /// + /// A GitHub URL + /// The repository and organization/owner name. private static (string org, string repo) GetRepoAndOrgNameFromUrl(string url) { var uri = new Uri(url); From 138456ac32a81b33f9274158fa4acbf4820ab8b4 Mon Sep 17 00:00:00 2001 From: Hootan HT Date: Sat, 22 Apr 2023 13:09:08 +0330 Subject: [PATCH 37/53] Fix rate limit exception. --- .../IServiceCollectionExtensions.cs | 6 +- .../Server/Api/appsettings.json | 81 ++++++++++--------- 2 files changed, 47 insertions(+), 40 deletions(-) diff --git a/src/CrystallineSociety/Server/Api/Extensions/IServiceCollectionExtensions.cs b/src/CrystallineSociety/Server/Api/Extensions/IServiceCollectionExtensions.cs index 3652552d..292396b6 100644 --- a/src/CrystallineSociety/Server/Api/Extensions/IServiceCollectionExtensions.cs +++ b/src/CrystallineSociety/Server/Api/Extensions/IServiceCollectionExtensions.cs @@ -185,6 +185,10 @@ public static void AddHealthChecks(this IServiceCollection services, IWebHostEnv private static GitHubClient CreateGitHubClient(IServiceProvider serviceProvider) { var productHeaderValue = new ProductHeaderValue("CS-System"); - return new GitHubClient(productHeaderValue); + var gitHubToken = serviceProvider.GetRequiredService().GetSection("GitHub")["GitHubAccessToken"]; + var tokenAuth = new Credentials(gitHubToken); + var client = new GitHubClient(productHeaderValue); + client.Credentials = tokenAuth; + return client; } } diff --git a/src/CrystallineSociety/Server/Api/appsettings.json b/src/CrystallineSociety/Server/Api/appsettings.json index d7422a3f..1f143f0a 100644 --- a/src/CrystallineSociety/Server/Api/appsettings.json +++ b/src/CrystallineSociety/Server/Api/appsettings.json @@ -1,44 +1,47 @@ { - "ConnectionStrings": { - "SqlServerConnectionString": "Data Source=.\\sqlexpress; Initial Catalog=CrystallineSocietyDb;Integrated Security=true;Application Name=Todo;TrustServerCertificate=True;" + "ConnectionStrings": { + "SqlServerConnectionString": "Data Source=.\\sqlexpress; Initial Catalog=CrystallineSocietyDb;Integrated Security=true;Application Name=Todo;TrustServerCertificate=True;" + }, + "GitHub": { + "GitHubAccessToken": "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" + }, + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AppSettings": { + "JwtSettings": { + "IdentityCertificatePassword": "P@ssw0rdP@ssw0rd", + "Issuer": "CrystallineSociety", + "Audience": "CrystallineSociety", + "NotBeforeMinutes": "0", + "ExpirationMinutes": "1440" }, - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft": "Warning", - "Microsoft.Hosting.Lifetime": "Information" - } + "IdentitySettings": { + "PasswordRequireDigit": "false", + "PasswordRequiredLength": "6", + "PasswordRequireNonAlphanumeric": "false", + "PasswordRequireUppercase": "false", + "PasswordRequireLowercase": "false", + "RequireUniqueEmail": "true", + "ConfirmationEmailResendDelay": "0.00:02:00", //Format: D.HH:mm:nn + "ResetPasswordEmailResendDelay": "0.00:02:00" //Format: D.HH:mm:nn }, - "AppSettings": { - "JwtSettings": { - "IdentityCertificatePassword": "P@ssw0rdP@ssw0rd", - "Issuer": "CrystallineSociety", - "Audience": "CrystallineSociety", - "NotBeforeMinutes": "0", - "ExpirationMinutes": "1440" - }, - "IdentitySettings": { - "PasswordRequireDigit": "false", - "PasswordRequiredLength": "6", - "PasswordRequireNonAlphanumeric": "false", - "PasswordRequireUppercase": "false", - "PasswordRequireLowercase": "false", - "RequireUniqueEmail": "true", - "ConfirmationEmailResendDelay": "0.00:02:00", //Format: D.HH:mm:nn - "ResetPasswordEmailResendDelay": "0.00:02:00" //Format: D.HH:mm:nn - }, - "EmailSettings": { - "Host": "LocalFolder", // Local folder means storing emails in bin\sent-emails folder (Recommended for testing purposes only) instead of sending them using smtp server. - "Port": "25", - "DefaulFromEmail": "info@CrystallineSociety.com", - "DefaultFromName": "CrystallineSociety", - "UserName": null, - "Password": null - }, - "HealthCheckSettings": { - "EnableHealthChecks": false - }, - "UserProfileImagePath": "Attachments/Profiles/" + "EmailSettings": { + "Host": "LocalFolder", // Local folder means storing emails in bin\sent-emails folder (Recommended for testing purposes only) instead of sending them using smtp server. + "Port": "25", + "DefaulFromEmail": "info@CrystallineSociety.com", + "DefaultFromName": "CrystallineSociety", + "UserName": null, + "Password": null }, - "AllowedHosts": "*" + "HealthCheckSettings": { + "EnableHealthChecks": false + }, + "UserProfileImagePath": "Attachments/Profiles/" + }, + "AllowedHosts": "*" } From f2a6f535723ae9cd013abd2caf4c36ca9de21e67 Mon Sep 17 00:00:00 2001 From: Hootan HT Date: Sat, 22 Apr 2023 13:19:43 +0330 Subject: [PATCH 38/53] Add new token. --- .../Server/Api/Extensions/IServiceCollectionExtensions.cs | 6 ++++-- src/CrystallineSociety/Server/Api/appsettings.json | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/CrystallineSociety/Server/Api/Extensions/IServiceCollectionExtensions.cs b/src/CrystallineSociety/Server/Api/Extensions/IServiceCollectionExtensions.cs index 292396b6..d40458bf 100644 --- a/src/CrystallineSociety/Server/Api/Extensions/IServiceCollectionExtensions.cs +++ b/src/CrystallineSociety/Server/Api/Extensions/IServiceCollectionExtensions.cs @@ -187,8 +187,10 @@ private static GitHubClient CreateGitHubClient(IServiceProvider serviceProvider) var productHeaderValue = new ProductHeaderValue("CS-System"); var gitHubToken = serviceProvider.GetRequiredService().GetSection("GitHub")["GitHubAccessToken"]; var tokenAuth = new Credentials(gitHubToken); - var client = new GitHubClient(productHeaderValue); - client.Credentials = tokenAuth; + var client = new GitHubClient(productHeaderValue) + { + Credentials = tokenAuth + }; return client; } } diff --git a/src/CrystallineSociety/Server/Api/appsettings.json b/src/CrystallineSociety/Server/Api/appsettings.json index 1f143f0a..f444f6ec 100644 --- a/src/CrystallineSociety/Server/Api/appsettings.json +++ b/src/CrystallineSociety/Server/Api/appsettings.json @@ -3,7 +3,7 @@ "SqlServerConnectionString": "Data Source=.\\sqlexpress; Initial Catalog=CrystallineSocietyDb;Integrated Security=true;Application Name=Todo;TrustServerCertificate=True;" }, "GitHub": { - "GitHubAccessToken": "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" + "GitHubAccessToken": "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" }, "Logging": { "LogLevel": { From 147beecf60fe7bc4c6d6c19b8fc8bd4ccf7ebb07 Mon Sep 17 00:00:00 2001 From: Hootan HT Date: Sat, 22 Apr 2023 13:26:30 +0330 Subject: [PATCH 39/53] Add new token. --- src/CrystallineSociety/Server/Api/appsettings.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CrystallineSociety/Server/Api/appsettings.json b/src/CrystallineSociety/Server/Api/appsettings.json index f444f6ec..9bd64729 100644 --- a/src/CrystallineSociety/Server/Api/appsettings.json +++ b/src/CrystallineSociety/Server/Api/appsettings.json @@ -3,7 +3,7 @@ "SqlServerConnectionString": "Data Source=.\\sqlexpress; Initial Catalog=CrystallineSocietyDb;Integrated Security=true;Application Name=Todo;TrustServerCertificate=True;" }, "GitHub": { - "GitHubAccessToken": "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" + "GitHubAccessToken": "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" }, "Logging": { "LogLevel": { From b2c3fe14ebf29eb3a804270f912436460cfe2cab Mon Sep 17 00:00:00 2001 From: Hootan HT Date: Sat, 22 Apr 2023 13:43:06 +0330 Subject: [PATCH 40/53] Add new token. --- .../Server/Api/Extensions/IServiceCollectionExtensions.cs | 2 +- src/CrystallineSociety/Server/Api/appsettings.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/CrystallineSociety/Server/Api/Extensions/IServiceCollectionExtensions.cs b/src/CrystallineSociety/Server/Api/Extensions/IServiceCollectionExtensions.cs index d40458bf..02ffd905 100644 --- a/src/CrystallineSociety/Server/Api/Extensions/IServiceCollectionExtensions.cs +++ b/src/CrystallineSociety/Server/Api/Extensions/IServiceCollectionExtensions.cs @@ -185,7 +185,7 @@ public static void AddHealthChecks(this IServiceCollection services, IWebHostEnv private static GitHubClient CreateGitHubClient(IServiceProvider serviceProvider) { var productHeaderValue = new ProductHeaderValue("CS-System"); - var gitHubToken = serviceProvider.GetRequiredService().GetSection("GitHub")["GitHubAccessToken"]; + var gitHubToken = "ghp_" + serviceProvider.GetRequiredService().GetSection("GitHub")["GitHubAccessToken"]; var tokenAuth = new Credentials(gitHubToken); var client = new GitHubClient(productHeaderValue) { diff --git a/src/CrystallineSociety/Server/Api/appsettings.json b/src/CrystallineSociety/Server/Api/appsettings.json index 9bd64729..d9a28fc0 100644 --- a/src/CrystallineSociety/Server/Api/appsettings.json +++ b/src/CrystallineSociety/Server/Api/appsettings.json @@ -3,7 +3,7 @@ "SqlServerConnectionString": "Data Source=.\\sqlexpress; Initial Catalog=CrystallineSocietyDb;Integrated Security=true;Application Name=Todo;TrustServerCertificate=True;" }, "GitHub": { - "GitHubAccessToken": "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" + "GitHubAccessToken": "5MttcI7TZ8cijBPklSxwUE5zKgOjKo1bnaTr" }, "Logging": { "LogLevel": { From ef850a17ab285137c645b93cd593a7ac3ebfc220 Mon Sep 17 00:00:00 2001 From: BSeyfi <42473071+BSeyfi@users.noreply.github.com> Date: Sat, 22 Apr 2023 14:52:29 +0330 Subject: [PATCH 41/53] Update code documentation --- .../Implementations/GitHubBadgeService.cs | 27 +++++++++---------- .../Services/Contracts/IGitHubBadgeService.cs | 22 +++++++-------- 2 files changed, 24 insertions(+), 25 deletions(-) diff --git a/src/CrystallineSociety/Server/Api/Services/Implementations/GitHubBadgeService.cs b/src/CrystallineSociety/Server/Api/Services/Implementations/GitHubBadgeService.cs index ad7dd948..821176d1 100644 --- a/src/CrystallineSociety/Server/Api/Services/Implementations/GitHubBadgeService.cs +++ b/src/CrystallineSociety/Server/Api/Services/Implementations/GitHubBadgeService.cs @@ -15,11 +15,11 @@ public partial class GitHubBadgeService : IGitHubBadgeService [AutoInject] public GitHubClient GitHubClient { get; set; } /// - /// Retrieves objects from badge files located at the GitHub URL provided. + /// Asynchronously retrieves objects from badge files located at the GitHub URL provided. /// This method performs a recursive search for files and selects those whose filename ends with `-badge.json`. /// /// GitHub folder URL containing badge files. - /// A list of parsed badge files. + /// A task that represents a list of parsed badge files. /// When the RepoId of light badge is null. /// When the Sha of light badge is null. public async Task> GetBadgesAsync(string folderUrl) @@ -46,20 +46,19 @@ public async Task> GetBadgesAsync(string folderUrl) } /// - /// Recursively loads and parses a lightweight version of badges specifications from a GitHub URL. + /// Asynchronously loads and parses a lightweight version of badges specifications from a GitHub URL pointing to a folder recursively. /// - /// The GitHub URL pointing to a folder containing badge file(s). All badge files will load recursively. Badge filename must ends with `-badge.json`. - /// A list of lightweight version of s. - public async Task> GetLightBadgesAsync(string url) + /// The GitHub URL pointing to a folder containing badge file(s). All badge files will load recursively. Badge filename must ends with `-badge.json`. + /// A task that represents a list of lightweight version of s. + public async Task> GetLightBadgesAsync(string folderUrl) { - var (orgName, repoName) = GetRepoAndOrgNameFromUrl(url); + var (orgName, repoName) = GetRepoAndOrgNameFromUrl(folderUrl); var repo = await GitHubClient.Repository.Get(orgName, repoName); var refs = await GitHubClient.Git.Reference.GetAll(repo.Id); - var lastSegment = GetLastSegmentFromUrl(url,refs,out var parentFolderPath); + var lastSegment = GetLastSegmentFromUrl(folderUrl,refs,out var parentFolderPath); var repositoryId = repo.Id; - - // ToDo: Handle if the given url is a file url instead of a folder url which is required here. + var folderContents = await GitHubClient.Repository.Content.GetAllContents(repositoryId, parentFolderPath); var folderSha = folderContents?.First(f => f.Name == lastSegment).Sha; var allContents = await GitHubClient.Git.Tree.GetRecursive(repositoryId, folderSha); @@ -71,10 +70,10 @@ public async Task> GetLightBadgesAsync(string url) } /// - /// Loads a badge specification from the given and parses it. + /// Asynchronously loads a badge specification from the given and parses it. /// /// The badge file GitHub URL. Badge filename must ends with `-badge.json`. - /// The parsed badge object. + /// A task that represents the parsed badge object. /// When unable to locate the GitHub repo branchName. /// When the given is not a valid badge file URL. /// When the loaded badge file has an incorrect format and cannot be parsed. @@ -108,11 +107,11 @@ public async Task GetBadgeAsync(string badgeUrl) } /// - /// Loads and parses a badge specification from a badge file on GitHub identified by the and parameters. + /// Asynchronously loads and parses a badge specification from a badge file on GitHub identified by the and parameters. /// /// The Id of the repository on GtiHub. /// The SHA Id of the file in the repository on GtiHub. - /// The parsed badge object. + /// A task that represents the parsed badge object. public async Task GetBadgeAsync(long repositoryId, string sha) { var badgeBlob = await GitHubClient.Git.Blob.Get(repositoryId, sha); diff --git a/src/CrystallineSociety/Shared/Shared/Services/Contracts/IGitHubBadgeService.cs b/src/CrystallineSociety/Shared/Shared/Services/Contracts/IGitHubBadgeService.cs index 9bc7a914..f0893ea2 100644 --- a/src/CrystallineSociety/Shared/Shared/Services/Contracts/IGitHubBadgeService.cs +++ b/src/CrystallineSociety/Shared/Shared/Services/Contracts/IGitHubBadgeService.cs @@ -10,38 +10,38 @@ namespace CrystallineSociety.Shared.Services.Contracts public interface IGitHubBadgeService { /// - /// Retrieves objects from badge files located at the GitHub URL provided. + /// Asynchronously retrieves objects from badge files located at the GitHub URL provided. /// This method performs a recursive search for files and selects those whose filename ends with `-badge.json`. /// /// GitHub folder URL containing badge files. - /// A list of parsed badge files. + /// A task that represents a list of parsed badge files. /// When the RepoId of light badge is null. /// When the Sha of light badge is null. Task> GetBadgesAsync(string folderUrl); /// - /// Loads a badge specification from the given and parses it. + /// Asynchronously loads a badge specification from the given and parses it. /// /// The badge file GitHub URL. Badge filename must ends with `-badge.json`. - /// The parsed badge object. + /// A task that represents the parsed badge object. /// When unable to locate the GitHub repo branchName. /// When the given is not a valid badge file URL. /// When the loaded badge file has an incorrect format and cannot be parsed. Task GetBadgeAsync(string badgeUrl); /// - /// Loads and parses a badge specification from a badge file on GitHub identified by the and parameters. + /// Asynchronously loads and parses a badge specification from a badge file on GitHub identified by the and parameters. /// /// The Id of the repository on GtiHub. /// The SHA Id of the file in the repository on GtiHub. - /// The parsed badge object. + /// A task that represents the parsed badge object. Task GetBadgeAsync(long repositoryId, string sha); - + /// - /// Recursively loads and parses a lightweight version of badges specifications from a GitHub URL. + /// Asynchronously loads and parses a lightweight version of badges specifications from a GitHub URL pointing to a folder recursively. /// - /// The GitHub URL pointing to a folder containing badge file(s). All badge files will load recursively. Badge filename must ends with `-badge.json`. - /// A list of lightweight version of s. - Task> GetLightBadgesAsync(string url); + /// The GitHub URL pointing to a folder containing badge file(s). All badge files will load recursively. Badge filename must ends with `-badge.json`. + /// A task that represents a list of lightweight version of s. + Task> GetLightBadgesAsync(string folderUrl); } } \ No newline at end of file From 4308a9957b4bf6e23118be3ee5aa1e7e44634972 Mon Sep 17 00:00:00 2001 From: Zahra Ahangari Date: Sat, 22 Apr 2023 20:11:00 +0330 Subject: [PATCH 42/53] Add more styles to components --- .../Shared/Components/BadgeContent.razor | 14 +++++++++--- .../Shared/Components/BadgeSystem.razor | 2 +- .../Client/Shared/Components/BadgeTree.razor | 8 +++---- .../Shared/Components/BadgeTree.razor.scss | 22 ++++++++++++++----- .../Pages/GitHubBadgeSystemExplorerPage.razor | 14 +++++++----- 5 files changed, 42 insertions(+), 18 deletions(-) diff --git a/src/CrystallineSociety/Client/Shared/Components/BadgeContent.razor b/src/CrystallineSociety/Client/Shared/Components/BadgeContent.razor index 12f2e0a5..c7c2c4b8 100644 --- a/src/CrystallineSociety/Client/Shared/Components/BadgeContent.razor +++ b/src/CrystallineSociety/Client/Shared/Components/BadgeContent.razor @@ -1,7 +1,15 @@ @using CrystallineSociety.Shared.Dtos.BadgeSystem @inherits AppComponentBase -@Badge?.Description - -

BadgeContent

+@if (Badge != null) +{ +
+

Badge Content

+ @Badge.Description +
+ @Badge.Level +
+ @Badge.Code +
+} diff --git a/src/CrystallineSociety/Client/Shared/Components/BadgeSystem.razor b/src/CrystallineSociety/Client/Shared/Components/BadgeSystem.razor index 1c08b7dd..03b27691 100644 --- a/src/CrystallineSociety/Client/Shared/Components/BadgeSystem.razor +++ b/src/CrystallineSociety/Client/Shared/Components/BadgeSystem.razor @@ -1,7 +1,7 @@ @using CrystallineSociety.Shared.Dtos.BadgeSystem @inherits AppComponentBase -
+
diff --git a/src/CrystallineSociety/Client/Shared/Components/BadgeTree.razor b/src/CrystallineSociety/Client/Shared/Components/BadgeTree.razor index b7670087..c589f9ed 100644 --- a/src/CrystallineSociety/Client/Shared/Components/BadgeTree.razor +++ b/src/CrystallineSociety/Client/Shared/Components/BadgeTree.razor @@ -4,11 +4,11 @@ @if (Badges != null) { + ItemSize="4" + Items="Badges" + Style="border: 1px #a19f9d solid; border-radius: 10px; height: auto; width: 50%"> -
+
Name: @badge.Code
diff --git a/src/CrystallineSociety/Client/Shared/Components/BadgeTree.razor.scss b/src/CrystallineSociety/Client/Shared/Components/BadgeTree.razor.scss index 99ec8953..9fe78dde 100644 --- a/src/CrystallineSociety/Client/Shared/Components/BadgeTree.razor.scss +++ b/src/CrystallineSociety/Client/Shared/Components/BadgeTree.razor.scss @@ -1,9 +1,21 @@ -.badge-name-color { - color: white; -} - .list { border: 1px #a19f9d solid; border-radius: 3px; height: auto; -} \ No newline at end of file +} + +.badge-row { + + &:hover { + cursor: pointer; + background-color: lightskyblue; + } + + .badge-name-color { + color: white; + + &:hover { + color: black + } + } +} diff --git a/src/CrystallineSociety/Client/Shared/Pages/GitHubBadgeSystemExplorerPage.razor b/src/CrystallineSociety/Client/Shared/Pages/GitHubBadgeSystemExplorerPage.razor index 2e580ff8..35878f5a 100644 --- a/src/CrystallineSociety/Client/Shared/Pages/GitHubBadgeSystemExplorerPage.razor +++ b/src/CrystallineSociety/Client/Shared/Pages/GitHubBadgeSystemExplorerPage.razor @@ -2,10 +2,14 @@ @using CrystallineSociety.Shared.Dtos.BadgeSystem @inherits AppComponentBase -
- GitHub URL: - - Load Badge System - @GitHubUrl +
+
+ GitHub URL: +
+ + +
+
+ @GitHubUrl
From 429351bc952f20e041a601703bf0e3985b8c0bfa Mon Sep 17 00:00:00 2001 From: Hootan HT Date: Sat, 22 Apr 2023 22:04:48 +0330 Subject: [PATCH 43/53] Fix small UI bug in Badge Tree. --- .../Client/Shared/Components/BadgeContent.razor | 6 +++--- .../Client/Shared/Components/BadgeSystem.razor | 2 +- .../Client/Shared/Components/BadgeTree.razor.scss | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/CrystallineSociety/Client/Shared/Components/BadgeContent.razor b/src/CrystallineSociety/Client/Shared/Components/BadgeContent.razor index c7c2c4b8..ae0508f8 100644 --- a/src/CrystallineSociety/Client/Shared/Components/BadgeContent.razor +++ b/src/CrystallineSociety/Client/Shared/Components/BadgeContent.razor @@ -5,11 +5,11 @@ {

Badge Content

- @Badge.Description +

Badge Code : @Badge.Code


- @Badge.Level +

Badge Level : @Badge.Level


- @Badge.Code +

Badge Description : @Badge.Description

} diff --git a/src/CrystallineSociety/Client/Shared/Components/BadgeSystem.razor b/src/CrystallineSociety/Client/Shared/Components/BadgeSystem.razor index 03b27691..e537cb12 100644 --- a/src/CrystallineSociety/Client/Shared/Components/BadgeSystem.razor +++ b/src/CrystallineSociety/Client/Shared/Components/BadgeSystem.razor @@ -1,7 +1,7 @@ @using CrystallineSociety.Shared.Dtos.BadgeSystem @inherits AppComponentBase -
+
diff --git a/src/CrystallineSociety/Client/Shared/Components/BadgeTree.razor.scss b/src/CrystallineSociety/Client/Shared/Components/BadgeTree.razor.scss index 9fe78dde..41353eb4 100644 --- a/src/CrystallineSociety/Client/Shared/Components/BadgeTree.razor.scss +++ b/src/CrystallineSociety/Client/Shared/Components/BadgeTree.razor.scss @@ -9,13 +9,13 @@ &:hover { cursor: pointer; background-color: lightskyblue; + + div { + color: black; + } } .badge-name-color { color: white; - - &:hover { - color: black - } } } From f430ab032e606f21e48a51dcab3d0e1f3bea1bb9 Mon Sep 17 00:00:00 2001 From: Hootan HT Date: Tue, 25 Apr 2023 20:13:15 +0330 Subject: [PATCH 44/53] Fix Badge click. --- .../Client/Shared/Components/BadgeTree.razor | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/CrystallineSociety/Client/Shared/Components/BadgeTree.razor b/src/CrystallineSociety/Client/Shared/Components/BadgeTree.razor index c589f9ed..4886aabb 100644 --- a/src/CrystallineSociety/Client/Shared/Components/BadgeTree.razor +++ b/src/CrystallineSociety/Client/Shared/Components/BadgeTree.razor @@ -4,12 +4,12 @@ @if (Badges != null) { + ItemSize="4" + Items="Badges" + Style="border: 1px #a19f9d solid; border-radius: 10px; height: auto; width: 50%"> -
-
Name: @badge.Code
+
+
Name: @badge.Code
From f9d9776fbb0a4180414e9b2b0a63079e66d5073a Mon Sep 17 00:00:00 2001 From: Afshin Alizadeh Date: Fri, 28 Apr 2023 20:01:12 +0330 Subject: [PATCH 45/53] Set up CI with Azure Pipelines [skip ci] --- azure-pipelines.yml | 76 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 azure-pipelines.yml diff --git a/azure-pipelines.yml b/azure-pipelines.yml new file mode 100644 index 00000000..229185db --- /dev/null +++ b/azure-pipelines.yml @@ -0,0 +1,76 @@ +trigger: +- main + +variables: + WEB_APP_DEPLOYMENT_TYPE: 'DefaultDeploymentType' + +jobs: + +- job: build_blazor_api_wasm + displayName: 'build blazor api + web assembly' + + pool: + vmImage: 'ubuntu-latest' + + steps: + - task: UseDotNet@2 + displayName: 'Setup .NET' + inputs: + useGlobalJson: true + workingDirectory: 'src' + + - task: UseDotNet@2 + displayName: 'Use dotnet sdk 6.x for LibSassBuilder' + inputs: + version: 6.x + + - task: Bash@3 + displayName: 'Restore workloads' + inputs: + targetType: 'inline' + script: 'dotnet workload restore src/Client/Web/CrystallineSociety.Client.Web.csproj -p:BlazorMode=BlazorWebAssembly' + + - task: Bash@3 + displayName: 'Switch to blazor web assembly' + inputs: + targetType: 'inline' + script: sed -i 's/Microsoft.NET.Sdk.Web/Microsoft.NET.Sdk.BlazorWebAssembly/g' src/Client/Web/CrystallineSociety.Client.Web.csproj + - task: Bash@3 + displayName: 'Build migrations bundle' + inputs: + targetType: 'inline' + script: | + dotnet tool install --global dotnet-ef --version 7.0.0 + dotnet ef migrations bundle --self-contained -r linux-x64 --project src/Server/Api/CrystallineSociety.Server.Api.csproj + failOnStderr: true + - task: Bash@3 + displayName: 'Install wasm-tools' + inputs: + targetType: 'inline' + script: dotnet workload install wasm-tools + + - task: Bash@3 + displayName: 'Build (To generate CSS/JS files)' + inputs: + targetType: 'inline' + script: 'dotnet build src/Client/Web/CrystallineSociety.Client.Web.csproj -p:Configuration=Release -p:BlazorMode=BlazorWebAssembly -p:WebAppDeploymentType="${{ variables.WEB_APP_DEPLOYMENT_TYPE }}"' + + - task: Bash@3 + displayName: 'Publish' + inputs: + targetType: 'inline' + script: 'dotnet publish src/Server/Api/CrystallineSociety.Server.Api.csproj -p:BlazorMode=BlazorWebAssembly -p:WebAppDeploymentType="${{ variables.WEB_APP_DEPLOYMENT_TYPE }}" -p:Configuration=Release -o api-web' + + - task: PublishPipelineArtifact@1 + displayName: Upload api-web artifact + inputs: + targetPath: 'api-web' + artifact: 'api-web-bundle' + publishLocation: 'pipeline' + + - task: PublishPipelineArtifact@1 + displayName: Upload ef migrations bundle + inputs: + targetPath: 'efbundle' + artifact: 'migrations-bundle' + publishLocation: 'pipeline' \ No newline at end of file From 88a9fe2066f9314753d99bfbb3f2f2e8a27aac67 Mon Sep 17 00:00:00 2001 From: Afshin Alizadeh Date: Fri, 28 Apr 2023 20:10:07 +0330 Subject: [PATCH 46/53] Update azure-pipelines.yml for Azure Pipelines --- azure-pipelines.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 229185db..649de293 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -34,14 +34,14 @@ jobs: displayName: 'Switch to blazor web assembly' inputs: targetType: 'inline' - script: sed -i 's/Microsoft.NET.Sdk.Web/Microsoft.NET.Sdk.BlazorWebAssembly/g' src/Client/Web/CrystallineSociety.Client.Web.csproj + script: sed -i 's/Microsoft.NET.Sdk.Web/Microsoft.NET.Sdk.BlazorWebAssembly/g' src/CrystallineSociety/Client/Web/CrystallineSociety.Client.Web.csproj - task: Bash@3 displayName: 'Build migrations bundle' inputs: targetType: 'inline' script: | dotnet tool install --global dotnet-ef --version 7.0.0 - dotnet ef migrations bundle --self-contained -r linux-x64 --project src/Server/Api/CrystallineSociety.Server.Api.csproj + dotnet ef migrations bundle --self-contained -r linux-x64 --project src/CrystallineSociety/Server/Api/CrystallineSociety.Server.Api.csproj failOnStderr: true - task: Bash@3 displayName: 'Install wasm-tools' @@ -53,13 +53,13 @@ jobs: displayName: 'Build (To generate CSS/JS files)' inputs: targetType: 'inline' - script: 'dotnet build src/Client/Web/CrystallineSociety.Client.Web.csproj -p:Configuration=Release -p:BlazorMode=BlazorWebAssembly -p:WebAppDeploymentType="${{ variables.WEB_APP_DEPLOYMENT_TYPE }}"' + script: 'dotnet build src/CrystallineSociety/Client/Web/CrystallineSociety.Client.Web.csproj -p:Configuration=Release -p:BlazorMode=BlazorWebAssembly -p:WebAppDeploymentType="${{ variables.WEB_APP_DEPLOYMENT_TYPE }}"' - task: Bash@3 displayName: 'Publish' inputs: targetType: 'inline' - script: 'dotnet publish src/Server/Api/CrystallineSociety.Server.Api.csproj -p:BlazorMode=BlazorWebAssembly -p:WebAppDeploymentType="${{ variables.WEB_APP_DEPLOYMENT_TYPE }}" -p:Configuration=Release -o api-web' + script: 'dotnet publish src/CrystallineSociety/Server/Api/CrystallineSociety.Server.Api.csproj -p:BlazorMode=BlazorWebAssembly -p:WebAppDeploymentType="${{ variables.WEB_APP_DEPLOYMENT_TYPE }}" -p:Configuration=Release -o api-web' - task: PublishPipelineArtifact@1 displayName: Upload api-web artifact From f6fca577b5d89719360aa4420a27183df4419867 Mon Sep 17 00:00:00 2001 From: Afshin Alizadeh Date: Fri, 28 Apr 2023 20:12:37 +0330 Subject: [PATCH 47/53] Update azure-pipelines.yml for Azure Pipelines --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 649de293..2a31a903 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -28,7 +28,7 @@ jobs: displayName: 'Restore workloads' inputs: targetType: 'inline' - script: 'dotnet workload restore src/Client/Web/CrystallineSociety.Client.Web.csproj -p:BlazorMode=BlazorWebAssembly' + script: 'dotnet workload restore src/CrystallineSociety/Client/Web/CrystallineSociety.Client.Web.csproj -p:BlazorMode=BlazorWebAssembly' - task: Bash@3 displayName: 'Switch to blazor web assembly' From 239745ebfa59fa558bac213354c607dbbcf72288 Mon Sep 17 00:00:00 2001 From: Afshin Alizadeh Date: Fri, 28 Apr 2023 20:43:46 +0330 Subject: [PATCH 48/53] Update azure-pipelines.yml for Azure Pipelines --- azure-pipelines.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 2a31a903..dc2ce107 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -1,5 +1,4 @@ -trigger: -- main +name: $(version).$(Build.BuildId) variables: WEB_APP_DEPLOYMENT_TYPE: 'DefaultDeploymentType' From 2756121da16b51485047307b50a01a6ad9bdf869 Mon Sep 17 00:00:00 2001 From: Afshin Alizadeh Date: Fri, 28 Apr 2023 20:50:43 +0330 Subject: [PATCH 49/53] Update azure-pipelines.yml for Azure Pipelines --- azure-pipelines.yml | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index dc2ce107..54360353 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -7,11 +7,22 @@ jobs: - job: build_blazor_api_wasm displayName: 'build blazor api + web assembly' - + pool: vmImage: 'ubuntu-latest' steps: + + - task: bleddynrichards.Assembly-Info-Task.Assembly-Info-NetCore.Assembly-Info-NetCore@2 + displayName: 'Set Assembly Manifest Data' + inputs: + Company: MelkRadar + Product: Bot + VersionNumber: '$(Build.BuildNumber)' + FileVersionNumber: '$(Build.BuildNumber)' + InformationalVersion: '$(Build.BuildNumber)' + PackageVersion: '$(Build.BuildNumber)' + - task: UseDotNet@2 displayName: 'Setup .NET' inputs: From 184f8e9c2016712a66c5c56876cc6ace715d6036 Mon Sep 17 00:00:00 2001 From: Afshin Alizadeh Date: Fri, 28 Apr 2023 20:54:01 +0330 Subject: [PATCH 50/53] Update azure-pipelines.yml for Azure Pipelines --- azure-pipelines.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 54360353..892eb7a8 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -12,8 +12,8 @@ jobs: vmImage: 'ubuntu-latest' steps: - - - task: bleddynrichards.Assembly-Info-Task.Assembly-Info-NetCore.Assembly-Info-NetCore@2 + + - task: Assembly-Info-NetCore@3 displayName: 'Set Assembly Manifest Data' inputs: Company: MelkRadar From db3585aea0e3405c32d23f4498448d7d6b3b84df Mon Sep 17 00:00:00 2001 From: Afshin Alizadeh Date: Fri, 28 Apr 2023 20:54:36 +0330 Subject: [PATCH 51/53] Update azure-pipelines.yml for Azure Pipelines --- azure-pipelines.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 892eb7a8..fc290737 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -16,8 +16,8 @@ jobs: - task: Assembly-Info-NetCore@3 displayName: 'Set Assembly Manifest Data' inputs: - Company: MelkRadar - Product: Bot + Company: Cs Internship + Product: Cs System VersionNumber: '$(Build.BuildNumber)' FileVersionNumber: '$(Build.BuildNumber)' InformationalVersion: '$(Build.BuildNumber)' From 6fb5e1f35c73da3532914076988113f79ebe8ae5 Mon Sep 17 00:00:00 2001 From: Afshin Alizadeh Date: Fri, 28 Apr 2023 21:01:17 +0330 Subject: [PATCH 52/53] Create Dockerfile --- Dockerfile | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 Dockerfile diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..facb2b13 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,4 @@ +FROM mcr.microsoft.com/dotnet/aspnet:7.0 +WORKDIR /app +COPY publish ./ +ENTRYPOINT ["dotnet", "CrystallineSociety.Server.Api.dll"] From 3b5139b5bb6a9762258d121188bd9e3acedcaa44 Mon Sep 17 00:00:00 2001 From: Afshin Alizadeh Date: Fri, 28 Apr 2023 21:06:33 +0330 Subject: [PATCH 53/53] Update azure-pipelines.yml for Azure Pipelines --- azure-pipelines.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index fc290737..ef1d2d16 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -83,4 +83,10 @@ jobs: inputs: targetPath: 'efbundle' artifact: 'migrations-bundle' - publishLocation: 'pipeline' \ No newline at end of file + publishLocation: 'pipeline' + - task: PublishPipelineArtifact@1 + inputs: + targetPath: 'Dockerfile' + artifact: 'Docker' + publishLocation: 'pipeline' + \ No newline at end of file