183 lines
7.7 KiB
C#
183 lines
7.7 KiB
C#
using JobsJobsJobs.Shared;
|
|
using JobsJobsJobs.Shared.Api;
|
|
using NodaTime;
|
|
using NodaTime.Serialization.SystemTextJson;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Net;
|
|
using System.Net.Http;
|
|
using System.Net.Http.Headers;
|
|
using System.Net.Http.Json;
|
|
using System.Text.Json;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace JobsJobsJobs.Client
|
|
{
|
|
/// <summary>
|
|
/// Functions used to access the API
|
|
/// </summary>
|
|
public static class ServerApi
|
|
{
|
|
/// <summary>
|
|
/// System.Text.Json options configured for NodaTime
|
|
/// </summary>
|
|
private static readonly JsonSerializerOptions _serializerOptions;
|
|
|
|
/// <summary>
|
|
/// Static constructor
|
|
/// </summary>
|
|
static ServerApi()
|
|
{
|
|
var options = new JsonSerializerOptions
|
|
{
|
|
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
|
|
};
|
|
options.ConfigureForNodaTime(DateTimeZoneProviders.Tzdb);
|
|
_serializerOptions = options;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create an API URL
|
|
/// </summary>
|
|
/// <param name="url">The URL to append to the API base URL</param>
|
|
/// <returns>The full URL to be used in HTTP requests</returns>
|
|
private static string ApiUrl(string url) => $"/api/{url}";
|
|
|
|
/// <summary>
|
|
/// Create an HTTP request with an authorization header
|
|
/// </summary>
|
|
/// <param name="state">The current application state</param>
|
|
/// <param name="url">The URL for the request (will be appended to the API root)</param>
|
|
/// <param name="method">The request method (optional, defaults to GET)</param>
|
|
/// <returns>A request with the header attached, ready for further manipulation</returns>
|
|
private static HttpRequestMessage WithHeader(AppState state, string url, HttpMethod? method = null)
|
|
{
|
|
var req = new HttpRequestMessage(method ?? HttpMethod.Get, ApiUrl(url));
|
|
req.Headers.Authorization = new AuthenticationHeaderValue("Bearer", state.Jwt);
|
|
return req;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set the JSON Web Token (JWT) bearer header for the given HTTP client
|
|
/// </summary>
|
|
/// <param name="http">The HTTP client whose authentication header should be set</param>
|
|
/// <param name="state">The current application state</param>
|
|
public static void SetJwt(HttpClient http, AppState state)
|
|
{
|
|
if (state.User != null)
|
|
{
|
|
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", state.Jwt);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Log on a user with the authorization code received from No Agenda Social
|
|
/// </summary>
|
|
/// <param name="http">The HTTP client to use for server communication</param>
|
|
/// <param name="authCode">The authorization code received from NAS</param>
|
|
/// <returns>The log on details if successful, an error if not</returns>
|
|
public static async Task<Result<LogOnSuccess>> LogOn(HttpClient http, string authCode)
|
|
{
|
|
try
|
|
{
|
|
var logOn = await http.GetFromJsonAsync<LogOnSuccess>(ApiUrl($"citizen/log-on/{authCode}"));
|
|
if (logOn == null) {
|
|
return Result<LogOnSuccess>.AsError(
|
|
"Unable to log on with No Agenda Social. This should never happen; contact @danieljsummers");
|
|
}
|
|
return Result<LogOnSuccess>.AsOk(logOn);
|
|
}
|
|
catch (HttpRequestException ex)
|
|
{
|
|
return Result<LogOnSuccess>.AsError($"Unable to log on with No Agenda Social: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Retrieve a citizen's profile
|
|
/// </summary>
|
|
/// <param name="http">The HTTP client to use for server communication</param>
|
|
/// <param name="state">The current application state</param>
|
|
/// <returns>The citizen's profile, null if it is not found, or an error message if one occurs</returns>
|
|
public static async Task<Result<Profile?>> RetrieveProfile(HttpClient http, AppState state)
|
|
{
|
|
var req = WithHeader(state, "profile/");
|
|
var res = await http.SendAsync(req);
|
|
return true switch
|
|
{
|
|
_ when res.StatusCode == HttpStatusCode.NoContent => Result<Profile?>.AsOk(null),
|
|
_ when res.IsSuccessStatusCode => Result<Profile?>.AsOk(
|
|
await res.Content.ReadFromJsonAsync<Profile>(_serializerOptions)),
|
|
_ => Result<Profile?>.AsError(await res.Content.ReadAsStringAsync()),
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Retrieve all continents
|
|
/// </summary>
|
|
/// <param name="http">The HTTP client to use for server communication</param>
|
|
/// <param name="state">The current application state</param>
|
|
/// <returns>The continents, or an error message if one occurs</returns>
|
|
public static async Task<Result<IEnumerable<Continent>>> AllContinents(HttpClient http, AppState state)
|
|
{
|
|
var req = WithHeader(state, "continent/all");
|
|
var res = await http.SendAsync(req);
|
|
if (res.IsSuccessStatusCode) {
|
|
var continents = await res.Content.ReadFromJsonAsync<IEnumerable<Continent>>();
|
|
return Result<IEnumerable<Continent>>.AsOk(continents ?? Enumerable.Empty<Continent>());
|
|
}
|
|
return Result<IEnumerable<Continent>>.AsError(await res.Content.ReadAsStringAsync());
|
|
}
|
|
|
|
/// <summary>
|
|
/// Retrieve many items from the given URL
|
|
/// </summary>
|
|
/// <typeparam name="T">The type of item expected</typeparam>
|
|
/// <param name="http">The HTTP client to use for server communication</param>
|
|
/// <param name="url">The API URL to call</param>
|
|
/// <returns>A result with the items, or an error if one occurs</returns>
|
|
/// <remarks>The caller is responsible for setting the JWT on the HTTP client</remarks>
|
|
public static async Task<Result<IEnumerable<T>>> RetrieveMany<T>(HttpClient http, string url)
|
|
{
|
|
try
|
|
{
|
|
var results = await http.GetFromJsonAsync<IEnumerable<T>>($"/api/{url}", _serializerOptions);
|
|
return Result<IEnumerable<T>>.AsOk(results ?? Enumerable.Empty<T>());
|
|
}
|
|
catch (HttpRequestException ex)
|
|
{
|
|
return Result<IEnumerable<T>>.AsError(ex.Message);
|
|
}
|
|
catch (JsonException ex)
|
|
{
|
|
return Result<IEnumerable<T>>.AsError($"Unable to parse result: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Retrieve one item from the given URL
|
|
/// </summary>
|
|
/// <typeparam name="T">The type of item expected</typeparam>
|
|
/// <param name="http">The HTTP client to use for server communication</param>
|
|
/// <param name="url">The API URL to call</param>
|
|
/// <returns>A result with the item (possibly null), or an error if one occurs</returns>
|
|
/// <remarks>The caller is responsible for setting the JWT on the HTTP client</remarks>
|
|
public static async Task<Result<T?>> RetrieveOne<T>(HttpClient http, string url)
|
|
{
|
|
try
|
|
{
|
|
return Result<T?>.AsOk(await http.GetFromJsonAsync<T>($"/api/{url}", _serializerOptions));
|
|
}
|
|
catch (HttpRequestException ex)
|
|
{
|
|
return Result<T?>.AsError(ex.Message);
|
|
}
|
|
catch (JsonException ex)
|
|
{
|
|
return Result<T?>.AsError($"Unable to parse result: {ex.Message}");
|
|
}
|
|
}
|
|
}
|
|
}
|