Skip to content

Commit

Permalink
Add FindSchools() utility method
Browse files Browse the repository at this point in the history
  • Loading branch information
ovicus committed Nov 2, 2019
1 parent 37427cf commit 2ab8a12
Show file tree
Hide file tree
Showing 5 changed files with 162 additions and 17 deletions.
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,17 @@ if (isAuthenticated)

You must call the `Authenticate()` method with a valid `username`, `password` and `schoolId`.

You can use the utility method `FindSchools()` to find out the `schoolId`.

```csharp
var schoolName = "Kingsdale";
var school = (await authService.FindSchools(schoolName)).FirstOrDefault();
if(school != null)
{
var schoolId = school.Id;
}
```

## Disclaimer
This library is not supported by ShowMyHomework.co.uk and is just the result of his author understanding about how the ShowMyHomework API works,
from observing its interactions with the public website. There is no public documentation about the API and it can change any time
Expand Down
31 changes: 21 additions & 10 deletions samples/Ovicus.ShowMyHomework.Sample/Program.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Ovicus.ShowMyHomework.Auth;
using System;
using System.Linq;
using System.Threading.Tasks;

namespace Ovicus.ShowMyHomework.Sample
Expand All @@ -16,21 +17,31 @@ static async Task Main(string[] args)
{
var authService = new AuthenticationService();

Console.Write("Enter School Id: ");
var schoolId = Console.ReadLine();
Console.Write("Enter School Name: ");
var schoolName = Console.ReadLine();

Console.Write("Enter Username: ");
var username = Console.ReadLine();
var school = (await authService.FindSchools(schoolName)).FirstOrDefault();

Console.Write("Enter Password: ");
var password = Console.ReadLine();
if (school != null)
{

Console.Write("Enter Username: ");
var username = Console.ReadLine();

bool isAuthenticated = await authService.Authenticate(username, password, schoolId);
Console.Write("Enter Password: ");
var password = Console.ReadLine();

if (isAuthenticated)
bool isAuthenticated = await authService.Authenticate(username, password, school.Id);

if (isAuthenticated)
{
accessToken = await authService.GetAccessToken();
await PrintTodos(accessToken);
}
}
else
{
accessToken = await authService.GetAccessToken();
await PrintTodos(accessToken);
Console.WriteLine($"School not found: {schoolName}");
}
}
else
Expand Down
18 changes: 15 additions & 3 deletions src/Ovicus.ShowMyHomework.Auth/AuthenticationService.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Ovicus.ShowMyHomework.Auth.Extensions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
Expand All @@ -11,7 +13,7 @@ namespace Ovicus.ShowMyHomework.Auth
public interface IAuthenticationService
{
Task<string> GetAccessToken();
Task<bool> Authenticate(string username, string password, string schoolId);
Task<bool> Authenticate(string username, string password, long schoolId);
}

public class AuthenticationService : IAuthenticationService
Expand Down Expand Up @@ -42,15 +44,15 @@ public AuthenticationService(string endpointBase, HttpClient client)
{
}

public async Task<bool> Authenticate(string username, string password, string schoolId)
public async Task<bool> Authenticate(string username, string password, long schoolId)
{
// Get new token with grant_type=password
var requestUrl = $"/oauth/token?client_id={OAuthClientId}&client_secret={OAuthClientSecret}";
var request = new HttpRequestMessage(HttpMethod.Post, requestUrl);

var keyValues = new List<KeyValuePair<string, string>>();
keyValues.Add(new KeyValuePair<string, string>("grant_type", "password"));
keyValues.Add(new KeyValuePair<string, string>("school_id", schoolId));
keyValues.Add(new KeyValuePair<string, string>("school_id", schoolId.ToString()));
keyValues.Add(new KeyValuePair<string, string>("username", username));
keyValues.Add(new KeyValuePair<string, string>("password", password));

Expand Down Expand Up @@ -100,5 +102,15 @@ private async Task<AuthToken> RefreshToken()

return _currentToken;
}

public async Task<IEnumerable<School>> FindSchools(string filterName)
{
var url = $"/api/public/school_search?filter={filterName}&limit=20";
var response = await _client.GetAsync(url);
var json = await response.EnsureSuccessStatusCode().Content.ReadAsStringAsync();
JObject root = JObject.Parse(json);
var schools = root.SelectToken("schools").ToObject<School[]>();
return schools.Where(s => s.IsActive);
}
}
}
24 changes: 24 additions & 0 deletions src/Ovicus.ShowMyHomework.Auth/School.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Text;

namespace Ovicus.ShowMyHomework.Auth
{
public class School
{
public long Id { get; set; }

public string Name { get; set; }

public string Address { get; set; }

public string Town { get; set; }

[JsonProperty("post_code")]
public string PostCode { get; set; }

[JsonProperty("is_active")]
public bool IsActive { get; set; }
}
}
95 changes: 91 additions & 4 deletions tests/Ovicus.ShowMyHomework.Tests/AuthenticationServiceTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,94 @@ namespace Ovicus.ShowMyHomework.Tests
public class AuthenticationServiceTests
{
private const string ApiBaseUrl = "https://api.showmyhomework.co.uk";


[Fact]
public async Task When_FindSchoolsCalledWithValidFilter_Then_ActiveMatchingSchoolsShouldBeReturned()
{
// ARRANGE
string responseBody = "{" +
"\"schools\": [" +
"{" +
"\"id\": 1234," +
"\"name\": \"Kings Langley School\"," +
"\"address\": \"Love Lane, Hertfordshire\"," +
"\"town\": \"Kings Langley\"," +
"\"post_code\": \"WD4 9HN\"," +
"\"subdomain\": \"kingslangley\"," +
"\"is_active\": true," +
"}," +
"{" +
"\"id\": 1235," +
"\"name\": \"Kingsley Academy\"," +
"\"address\": \"Cecil Road\"," +
"\"town\": \"Hounslow\"," +
"\"post_code\": \"TW3 1AX\"," +
"\"subdomain\": \"kingsleyacademy\"," +
"\"is_active\": false," +
"}," +
"{ " +
"\"id\": 1236," +
"\"name\": \"Kingsdale Foundation School\"," +
"\"address\": \"Alleyn Park, Dulwich\"," +
"\"town\": \"London\"," +
"\"post_code\": \"SE21 8SQ\"," +
"\"subdomain\": \"kingsdale\"," +
"\"is_active\": true," +
"}]}";

Mock<HttpMessageHandler> handlerMock = MockHttpMessageHandlerBuilder.Build(responseBody, HttpStatusCode.OK);
// use real http client with mocked handler here
HttpClient httpClient = new HttpClient(handlerMock.Object);

var sut = new AuthenticationService(ApiBaseUrl, httpClient);

// ACT
var result = await sut.FindSchools("Kings");

// ASSERT
result.Should().NotBeNull();
result.Should().HaveCount(2);
result.Should().OnlyContain(s => s.IsActive);

handlerMock.Protected().Verify(
"SendAsync",
Times.Exactly(1), // we expected a single external request
ItExpr.Is<HttpRequestMessage>(req =>
req.Method == HttpMethod.Get // we expected a POST request
),
ItExpr.IsAny<CancellationToken>()
);
}

[Fact]
public async Task When_FindSchoolsCalledWithInvalidFilter_Then_ShouldReturnNoResults()
{
// ARRANGE
string responseBody = "{ \"schools\": [] }";

Mock<HttpMessageHandler> handlerMock = MockHttpMessageHandlerBuilder.Build(responseBody, HttpStatusCode.OK);
// use real http client with mocked handler here
HttpClient httpClient = new HttpClient(handlerMock.Object);

var sut = new AuthenticationService(ApiBaseUrl, httpClient);

// ACT
var result = await sut.FindSchools("NonExistantSchoolName");

// ASSERT
result.Should().NotBeNull();
result.Should().BeEmpty();

handlerMock.Protected().Verify(
"SendAsync",
Times.Exactly(1), // we expected a single external request
ItExpr.Is<HttpRequestMessage>(req =>
req.Method == HttpMethod.Get // we expected a POST request
),
ItExpr.IsAny<CancellationToken>()
);
}

[Fact]
public async Task When_ValidCredentialsAreProvided_Then_AuthenticationSucceedAndAccessTokenCreated()
{
Expand All @@ -38,7 +125,7 @@ public async Task When_ValidCredentialsAreProvided_Then_AuthenticationSucceedAnd
var sut = new AuthenticationService(ApiBaseUrl, httpClient);

// ACT
var result = await sut.Authenticate("username", "$3cret", "1234");
var result = await sut.Authenticate("username", "$3cret", 1234);
var token = await sut.GetAccessToken();

// ASSERT
Expand Down Expand Up @@ -69,7 +156,7 @@ public async Task When_InvalidCredentialsAreProvided_Then_AuthenticationFailsAnd
var sut = new AuthenticationService(ApiBaseUrl, httpClient);

// ACT
var result = await sut.Authenticate("username", "inv@lidPass", "1234");
var result = await sut.Authenticate("username", "inv@lidPass", 1234);

// ASSERT
result.Should().BeFalse();
Expand Down Expand Up @@ -139,7 +226,7 @@ public async Task When_TokenIsExpired_Should_TryRefresh()
// This will make a request to the authentication endpoint and retrieve the (expired) token
// This method does not check the validity of the token, just simply trust
// the authentication server will issue a valid token on successful authentication
var authResult = await sut.Authenticate("username", "$3cret", "1234");
var authResult = await sut.Authenticate("username", "$3cret", 1234);

// ARRANGE (2nd stage)

Expand Down

0 comments on commit 2ab8a12

Please sign in to comment.