From b9824a7327e1e4c568d1e379f9f6002ef4a99b4f Mon Sep 17 00:00:00 2001 From: Alex Mickelson Date: Wed, 4 Jan 2023 20:26:15 -0700 Subject: [PATCH] commit pre-purge --- .../Features/SemesterPlannerTests.cs | 10 +++ Management.Test/Management.Test.csproj | 7 ++ Management.Test/Models/TermTests.cs | 22 +++++++ .../Services/CanvasServiceTests.cs | 39 +++++++++++ .../Pages/{FetchData.razor => Testing.razor} | 2 +- Management.Web/Shared/NavMenu.razor | 4 +- Management/Management.csproj | 4 ++ .../CanvasModel/Courses/CalendarLinkModel.cs | 11 ++-- .../Models/CanvasModel/Courses/TermModel.cs | 23 ++----- .../EnrollmentTerms/EnrollmentTermModel.cs | 58 +++++----------- .../RedundantEnrollmentTermsResponse.cs | 12 ++-- Management/Services/CanvasService.cs | 66 ++++++++++++++++++- Management/Services/IWebRequestor.cs | 7 ++ Management/Services/WebRequestor.cs | 26 ++++++++ README.md | 4 +- requests/semester.http | 9 +++ 16 files changed, 229 insertions(+), 75 deletions(-) create mode 100644 Management.Test/Features/SemesterPlannerTests.cs create mode 100644 Management.Test/Models/TermTests.cs create mode 100644 Management.Test/Services/CanvasServiceTests.cs rename Management.Web/Pages/{FetchData.razor => Testing.razor} (94%) create mode 100644 Management/Services/IWebRequestor.cs create mode 100644 Management/Services/WebRequestor.cs create mode 100644 requests/semester.http diff --git a/Management.Test/Features/SemesterPlannerTests.cs b/Management.Test/Features/SemesterPlannerTests.cs new file mode 100644 index 0000000..bbda40b --- /dev/null +++ b/Management.Test/Features/SemesterPlannerTests.cs @@ -0,0 +1,10 @@ +namespace Management.Test; + +public class SemesterPlannerTests +{ + [Test] + public void TestCanCreatePlannerFromCanvasSemester() + { + // var + } +} \ No newline at end of file diff --git a/Management.Test/Management.Test.csproj b/Management.Test/Management.Test.csproj index a86f2bf..b4e1701 100644 --- a/Management.Test/Management.Test.csproj +++ b/Management.Test/Management.Test.csproj @@ -9,11 +9,18 @@ + + + + + + + diff --git a/Management.Test/Models/TermTests.cs b/Management.Test/Models/TermTests.cs new file mode 100644 index 0000000..90862fa --- /dev/null +++ b/Management.Test/Models/TermTests.cs @@ -0,0 +1,22 @@ +public class DeserializationTests +{ + [Test] + public void TestTerm() + { + + var canvasContentResponse = @"{ + ""enrollment_terms"": [ + { + ""id"": 1, + ""name"": ""one"", + ""start_at"": 2022-01-01T00:00:00Z, + ""end_at"": 2022-02-01T00:00:00Z, + ""created_at"": ""2011-04-26T23:34:35Z"", + ""workflow_state"": ""active"", + ""grading_period_group_id"": null + }, + }"; + + + } +} \ No newline at end of file diff --git a/Management.Test/Services/CanvasServiceTests.cs b/Management.Test/Services/CanvasServiceTests.cs new file mode 100644 index 0000000..8c8b043 --- /dev/null +++ b/Management.Test/Services/CanvasServiceTests.cs @@ -0,0 +1,39 @@ +using CanvasModel.Courses; +using CanvasModel.EnrollmentTerms; +using FluentAssertions; +using Moq; +using RestSharp; +using System.Net; + +namespace Management.Test; + +public class CanvasServiceTests +{ + [Test] + public async Task CanReadCanvasSemesters() + { + var expectedTerms = new EnrollmentTermModel[] { + new EnrollmentTermModel( + Id: 1, + Name: "one", + StartAt: new DateTime(2022, 1, 1), + EndAt: new DateTime(2022, 2, 1) + ), + }; + var data = new RedundantEnrollmentTermsResponse(EnrollmentTerms: expectedTerms); + var response = new RestResponse(); + response.Data = data; + + var mockRequestor = new Mock(); + mockRequestor + .Setup(s => s.GetAsync(It.IsAny())) + .ReturnsAsync(response); + + var service = new CanvasService(mockRequestor.Object); + var canvasTerms = await service.GetTerms(); + + canvasTerms.Should().BeEquivalentTo(expectedTerms); + + } + +} \ No newline at end of file diff --git a/Management.Web/Pages/FetchData.razor b/Management.Web/Pages/Testing.razor similarity index 94% rename from Management.Web/Pages/FetchData.razor rename to Management.Web/Pages/Testing.razor index 719c75a..4670cc9 100644 --- a/Management.Web/Pages/FetchData.razor +++ b/Management.Web/Pages/Testing.razor @@ -1,4 +1,4 @@ -@page "/fetchdata" +@page "/testing" @using Management.Web.Data @inject WeatherForecastService ForecastService diff --git a/Management.Web/Shared/NavMenu.razor b/Management.Web/Shared/NavMenu.razor index e3a3158..a1a1412 100644 --- a/Management.Web/Shared/NavMenu.razor +++ b/Management.Web/Shared/NavMenu.razor @@ -20,8 +20,8 @@ diff --git a/Management/Management.csproj b/Management/Management.csproj index 4658cbf..b12872f 100644 --- a/Management/Management.csproj +++ b/Management/Management.csproj @@ -6,4 +6,8 @@ enable + + + + diff --git a/Management/Models/CanvasModel/Courses/CalendarLinkModel.cs b/Management/Models/CanvasModel/Courses/CalendarLinkModel.cs index b3b0cc1..276412e 100644 --- a/Management/Models/CanvasModel/Courses/CalendarLinkModel.cs +++ b/Management/Models/CanvasModel/Courses/CalendarLinkModel.cs @@ -1,8 +1,5 @@ - - namespace CanvasModel.Courses; -public struct CalendarLinkModel -{ - [JsonPropertyName("ics")] - public string Ics { get; set; } -} \ No newline at end of file +public record CalendarLinkModel +( + [property: JsonPropertyName("ics")] string Ics +); \ No newline at end of file diff --git a/Management/Models/CanvasModel/Courses/TermModel.cs b/Management/Models/CanvasModel/Courses/TermModel.cs index e39f7ff..2447ccb 100644 --- a/Management/Models/CanvasModel/Courses/TermModel.cs +++ b/Management/Models/CanvasModel/Courses/TermModel.cs @@ -1,17 +1,8 @@ - namespace CanvasModel.Courses; -public class TermModel -{ - - [JsonPropertyName("id")] - public ulong Id { get; set; } - - [JsonPropertyName("name")] - public string Name { get; set; } - - [JsonPropertyName("start_at")] - public DateTime? StartAt { get; set; } - - [JsonPropertyName("end_at")] - public DateTime? EndAt { get; set; } -} \ No newline at end of file +public record TermModel +( + [property: JsonPropertyName("id")] ulong Id, + [property: JsonPropertyName("name")] string Name, + [property: JsonPropertyName("start_at")] DateTime? StartAt, + [property: JsonPropertyName("end_at")] DateTime? EndAt +); diff --git a/Management/Models/CanvasModel/EnrollmentTerms/EnrollmentTermModel.cs b/Management/Models/CanvasModel/EnrollmentTerms/EnrollmentTermModel.cs index 0a7e08d..2858bf7 100644 --- a/Management/Models/CanvasModel/EnrollmentTerms/EnrollmentTermModel.cs +++ b/Management/Models/CanvasModel/EnrollmentTerms/EnrollmentTermModel.cs @@ -1,43 +1,21 @@ -using System; -using System.Collections.Generic; - - namespace CanvasModel.EnrollmentTerms; -public class EnrollmentTermModel -{ - [JsonPropertyName("id")] - public ulong Id { get; set; } - [JsonPropertyName("sis_term_id")] - public string SisTermId { get; set; } +public record EnrollmentTermModel +( + [property: JsonPropertyName("id")] ulong Id, + [property: JsonPropertyName("name")] string Name, + [property: JsonPropertyName("sis_term_id")] string? SisTermId = null, + [property: JsonPropertyName("sis_import_id")] ulong? SisImportId = null, + [property: JsonPropertyName("start_at")] DateTime? StartAt = null, + [property: JsonPropertyName("end_at")] DateTime? EndAt = null, + [property: JsonPropertyName("grading_period_group_id")] ulong? GradingPeriodGroupId = null, + [property: JsonPropertyName("workflow_state")] string? WorkflowState = null, + [property: JsonPropertyName("overrides")] + Dictionary? Overrides = null +); - [JsonPropertyName("sis_import_id")] - public ulong? SisImportId { get; set; } - - [JsonPropertyName("name")] - public string Name { get; set; } - - [JsonPropertyName("start_at")] - public DateTime? StartAt { get; set; } - - [JsonPropertyName("end_at")] - public DateTime? EndAt { get; set; } - - [JsonPropertyName("grading_period_group_id")] - public ulong? GradingPeriodGroupId { get; set; } - - [JsonPropertyName("workflow_state")] - public string WorkflowState { get; set; } - - [JsonPropertyName("overrides")] - public Dictionary Overrides { get; set; } -} - -public struct EnrollmentTermDateOverrideModel -{ - [JsonPropertyName("start_at")] - public DateTime? StartAt { get; set; } - - [JsonPropertyName("end_at")] - public DateTime? EndAt { get; set; } -} +public record EnrollmentTermDateOverrideModel +( + [property: JsonPropertyName("start_at")] DateTime? StartAt = null, + [property: JsonPropertyName("end_at")] DateTime? EndAt = null +); diff --git a/Management/Models/CanvasModel/EnrollmentTerms/RedundantEnrollmentTermsResponse.cs b/Management/Models/CanvasModel/EnrollmentTerms/RedundantEnrollmentTermsResponse.cs index 9261307..1178ea4 100644 --- a/Management/Models/CanvasModel/EnrollmentTerms/RedundantEnrollmentTermsResponse.cs +++ b/Management/Models/CanvasModel/EnrollmentTerms/RedundantEnrollmentTermsResponse.cs @@ -1,9 +1,7 @@ -using System.Collections.Generic; - namespace CanvasModel.EnrollmentTerms; -public struct RedundantEnrollmentTermsResponse -{ - [JsonPropertyName("enrollment_terms")] - public IEnumerable EnrollmentTerms { get; set; } -} +public record RedundantEnrollmentTermsResponse +( + [property: JsonPropertyName("enrollment_terms")] + IEnumerable EnrollmentTerms +); diff --git a/Management/Services/CanvasService.cs b/Management/Services/CanvasService.cs index 639c842..85390f7 100644 --- a/Management/Services/CanvasService.cs +++ b/Management/Services/CanvasService.cs @@ -1,4 +1,68 @@ +using CanvasModel.Courses; +using CanvasModel.EnrollmentTerms; +using RestSharp; public class CanvasService { - + private const string BaseUrl = "https://snow.instructure.com/api/v1/"; + private readonly IWebRequestor webRequestor; + private RestClient client; + private string courseid { get; } + public CanvasService(IWebRequestor webRequestor) + { + courseid = "774898"; + this.webRequestor = webRequestor; + } + + public async Task> GetTerms() + { + var url = $"accounts/10/terms"; + + var request = new RestRequest(url); + var terms = await PaginatedRequest(request); + return terms; + } + + private async Task> PaginatedRequest(RestRequest request) + { + var requestCount = 1; + request.AddQueryParameter("per_page", "100"); + IEnumerable returnData = new T[] { }; + RestResponse response = await webRequestor.GetAsync(request); + returnData = returnData.Concat(response.Data); + + var nextUrl = getNextUrl(response); + + while (nextUrl is not null) + { + requestCount += 1; + var nextRequest = new RestRequest(nextUrl); + var nextResponse = await webRequestor.GetAsync(nextRequest); + if (nextResponse.Data is not null) + returnData = returnData.Concat(nextResponse.Data); + nextUrl = getNextUrl(nextResponse); + } + + System.Console.WriteLine($"Requesting {typeof(T)} took {requestCount} requests"); + + return returnData; + } + + + private static string? getNextUrl(RestResponse response) => response.Headers? + .ToList() + .Find(h => h.Name == "Link")? + .Value? + .ToString()? + .Split(",") + .Where(url => url.Contains("rel=\"next\"")) + .FirstOrDefault()? + .Split(";") + .FirstOrDefault()? + .TrimEnd('>') + .TrimStart('<') + .Replace(" ", "") + .Replace(BaseUrl, ""); + + + } \ No newline at end of file diff --git a/Management/Services/IWebRequestor.cs b/Management/Services/IWebRequestor.cs new file mode 100644 index 0000000..cda00d0 --- /dev/null +++ b/Management/Services/IWebRequestor.cs @@ -0,0 +1,7 @@ +using RestSharp; + +public interface IWebRequestor +{ + Task> GetManyAsync(RestRequest request); + Task> GetAsync(RestRequest request); +} diff --git a/Management/Services/WebRequestor.cs b/Management/Services/WebRequestor.cs new file mode 100644 index 0000000..2a2ea56 --- /dev/null +++ b/Management/Services/WebRequestor.cs @@ -0,0 +1,26 @@ +using RestSharp; + +public class WebRequestor : IWebRequestor +{ + private const string BaseUrl = "https://snow.instructure.com/api/v1/"; + private string token; + private RestClient client; + private string courseid { get; } + public WebRequestor(RestClient client) + { + // token = Environment.GetEnvironmentVariable("CANVAS_TOKEN"); + // client = new RestClient(BaseUrl); + // client.AddDefaultHeader("Authorization", $"Bearer {token}"); + + this.client = client; + courseid = "774898"; + } + public async Task> GetManyAsync(RestRequest request) + { + return await client.ExecuteGetAsync(request); + } + public async Task> GetAsync(RestRequest request) + { + return await client.ExecuteGetAsync(request); + } +} \ No newline at end of file diff --git a/README.md b/README.md index 66864b3..192ac61 100644 --- a/README.md +++ b/README.md @@ -3,4 +3,6 @@ install specflow template `dotnet new install Specflow.Templates.DotNet` -view templates with `dotnet new -l` \ No newline at end of file +view templates with `dotnet new -l` + +find outdated packages `dotnet list package --outdated` \ No newline at end of file diff --git a/requests/semester.http b/requests/semester.http new file mode 100644 index 0000000..0b041cb --- /dev/null +++ b/requests/semester.http @@ -0,0 +1,9 @@ + +### +GET https://snow.instructure.com/api/v1/account_calendars +Authorization: Bearer {{$dotenv CANVAS_TOKEN}} + +### +GET https://snow.instructure.com/api/v1/accounts/10/terms +Authorization: Bearer {{$dotenv CANVAS_TOKEN}} +