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
{
///
/// Functions used to access the API
///
public static class ServerApi
{
///
/// System.Text.Json options configured for NodaTime
///
private static readonly JsonSerializerOptions _serializerOptions;
///
/// Static constructor
///
static ServerApi()
{
var options = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};
options.ConfigureForNodaTime(DateTimeZoneProviders.Tzdb);
_serializerOptions = options;
}
///
/// Create an API URL
///
/// The URL to append to the API base URL
/// The full URL to be used in HTTP requests
private static string ApiUrl(string url) => $"/api/{url}";
///
/// Create an HTTP request with an authorization header
///
/// The current application state
/// The URL for the request (will be appended to the API root)
/// The request method (optional, defaults to GET)
/// A request with the header attached, ready for further manipulation
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;
}
///
/// Set the JSON Web Token (JWT) bearer header for the given HTTP client
///
/// The HTTP client whose authentication header should be set
/// The current application state
public static void SetJwt(HttpClient http, AppState state)
{
if (state.User != null)
{
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", state.Jwt);
}
}
///
/// Log on a user with the authorization code received from No Agenda Social
///
/// The HTTP client to use for server communication
/// The authorization code received from NAS
/// The log on details if successful, an error if not
public static async Task> LogOn(HttpClient http, string authCode)
{
try
{
var logOn = await http.GetFromJsonAsync(ApiUrl($"citizen/log-on/{authCode}"));
if (logOn == null) {
return Result.AsError(
"Unable to log on with No Agenda Social. This should never happen; contact @danieljsummers");
}
return Result.AsOk(logOn);
}
catch (HttpRequestException ex)
{
return Result.AsError($"Unable to log on with No Agenda Social: {ex.Message}");
}
}
///
/// Retrieve a citizen's profile
///
/// The HTTP client to use for server communication
/// The current application state
/// The citizen's profile, null if it is not found, or an error message if one occurs
public static async Task> 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.AsOk(null),
_ when res.IsSuccessStatusCode => Result.AsOk(
await res.Content.ReadFromJsonAsync(_serializerOptions)),
_ => Result.AsError(await res.Content.ReadAsStringAsync()),
};
}
///
/// Retrieve all continents
///
/// The HTTP client to use for server communication
/// The current application state
/// The continents, or an error message if one occurs
public static async Task>> 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>();
return Result>.AsOk(continents ?? Enumerable.Empty());
}
return Result>.AsError(await res.Content.ReadAsStringAsync());
}
///
/// Retrieve many items from the given URL
///
/// The type of item expected
/// The HTTP client to use for server communication
/// The API URL to call
/// A result with the items, or an error if one occurs
/// The caller is responsible for setting the JWT on the HTTP client
public static async Task>> RetrieveMany(HttpClient http, string url)
{
try
{
var results = await http.GetFromJsonAsync>($"/api/{url}", _serializerOptions);
return Result>.AsOk(results ?? Enumerable.Empty());
}
catch (HttpRequestException ex)
{
return Result>.AsError(ex.Message);
}
catch (JsonException ex)
{
return Result>.AsError($"Unable to parse result: {ex.Message}");
}
}
///
/// Retrieve one item from the given URL
///
/// The type of item expected
/// The HTTP client to use for server communication
/// The API URL to call
/// A result with the item (possibly null), or an error if one occurs
/// The caller is responsible for setting the JWT on the HTTP client
public static async Task> RetrieveOne(HttpClient http, string url)
{
try
{
return Result.AsOk(await http.GetFromJsonAsync($"/api/{url}", _serializerOptions));
}
catch (HttpRequestException ex)
{
return Result.AsError(ex.Message);
}
catch (JsonException ex)
{
return Result.AsError($"Unable to parse result: {ex.Message}");
}
}
}
}