chore: initial commit

This commit is contained in:
Matthias Langhard
2020-12-07 22:00:24 +01:00
commit 75a687e31e
20 changed files with 507 additions and 0 deletions

View File

@@ -0,0 +1,14 @@
using System;
namespace Novaloop.PaymoApi.Exceptions
{
public class PaymoApiException : Exception
{
public PaymoApiException(int statusCode, string message) : base($"[{statusCode}]: {message})")
{
StatusCode = statusCode;
}
public int StatusCode { get; }
}
}

View File

@@ -0,0 +1,37 @@
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
namespace Novaloop.PaymoApi.Extensions
{
internal static class HttpClientExtensions
{
internal static void SetApiKeyHeader(this HttpClient client, string apiKey)
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Base64Encode($"{apiKey}:random"));
}
internal static async Task<HttpResponseMessage> PatchAsync(this HttpClient client, string uri, HttpContent content)
{
var method = new HttpMethod("PATCH");
if (client.BaseAddress is null)
{
throw new ArgumentException("Can not handle 'BaseAddress' null value configuration.");
}
var request = new HttpRequestMessage(method, new Uri(CombineBaseUrlWithSegment(client.BaseAddress.ToString(), uri))) {Content = content};
return await client.SendAsync(request);
}
private static string CombineBaseUrlWithSegment(string uri1, string uri2)
{
return $"{uri1.TrimEnd('/')}/{uri2.TrimStart('/')}";
}
private static string Base64Encode(string plainText)
{
return Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(plainText));
}
}
}

View File

@@ -0,0 +1,17 @@
using System.Net.Http;
using System.Threading.Tasks;
using Novaloop.PaymoApi.Exceptions;
namespace Novaloop.PaymoApi.Extensions
{
internal static class HttpResponseMessageExtensions
{
internal static async Task ThrowExceptionWithDetailsIfUnsuccessful(this HttpResponseMessage response)
{
if (!response.IsSuccessStatusCode)
{
throw new PaymoApiException((int) response.StatusCode, await response.Content.ReadAsStringAsync());
}
}
}
}

View File

@@ -0,0 +1,20 @@
using System;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Novaloop.PaymoApi.Shared;
using Novaloop.PaymoApi.Tasks;
namespace Novaloop.PaymoApi.Extensions
{
public static class PaymoApiExtensions
{
public static IServiceCollection AddPaymoApi(this IServiceCollection services, Action<PaymoApiOptions> options)
{
services.Configure(options);
var resolvedOptions = (IOptions<PaymoApiOptions>) services.BuildServiceProvider().GetService(typeof(IOptions<PaymoApiOptions>));
services.AddHttpClient<PaymoApiClient>(client => { client.BaseAddress = new Uri(resolvedOptions.Value.BaseUrl); });
services.AddTransient<IPaymoTasksApiService, PaymoTasksApiService>();
return services;
}
}
}

View File

@@ -0,0 +1,8 @@
namespace Novaloop.PaymoApi.Extensions
{
public class PaymoApiOptions
{
public string BaseUrl { get; set; } = "https://app.paymoapp.com/api";
public string ApiToken { get; set; }
}
}

View File

@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<PackageId>Novaloop.PaymoApi</PackageId>
<title>Access your paymo instance for asp.net core</title>
<PackageTags>api;paymo;asp.net core;</PackageTags>
<Version>0.1.0</Version>
<Authors>Matthias Langhard</Authors>
<Company>Novaloop AG</Company>
<PackageProjectUrl>https://gitlab.com/novaloop-oss/novaloop.paymoapi</PackageProjectUrl>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNet.WebApi.Client" Version="5.2.7" />
<PackageReference Include="Microsoft.Extensions.Http" Version="5.0.0" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,14 @@
using System.Net.Http;
namespace Novaloop.PaymoApi.Shared
{
public class PaymoApiClient
{
public PaymoApiClient(HttpClient client)
{
Client = client;
}
public HttpClient Client { get; }
}
}

View File

@@ -0,0 +1,11 @@
using System.Threading.Tasks;
using Novaloop.PaymoApi.Tasks.Models;
namespace Novaloop.PaymoApi.Tasks
{
public interface IPaymoTasksApiService
{
Task<GetTasksResponse> GetTask(int taskId);
Task<CreateTaskResponse> CreateTask(CreateTaskRequest createTask);
}
}

View File

@@ -0,0 +1,12 @@
using System.Collections.Generic;
namespace Novaloop.PaymoApi.Tasks.Models
{
public class CreateTaskRequest
{
public string Name { get; set; }
public string Description { get; set; }
public int TasklistId { get; set; }
public List<int> Users { get; set; }
}
}

View File

@@ -0,0 +1,7 @@
namespace Novaloop.PaymoApi.Tasks.Models
{
public class CreateTaskResponse : PaymoTask
{
}
}

View File

@@ -0,0 +1,9 @@
using System.Collections.Generic;
namespace Novaloop.PaymoApi.Tasks.Models
{
public class GetTasksResponse
{
public IEnumerable<PaymoTask> Tasks { get; set; }
}
}

View File

@@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
namespace Novaloop.PaymoApi.Tasks.Models
{
public class PaymoTask
{
public int Id { get; set; }
public string Name { get; set; }
public string Code { get; set; }
public int ProjectId { get; set; }
public int TasklistId { get; set; }
public int UserId { get; set; }
public bool Complete { get; set; }
public bool Billable { get; set; }
public int Seq { get; set; }
public string Description { get; set; }
public object PricePerHour { get; set; }
public object DueDate { get; set; }
public object BudgetHours { get; set; }
public List<int> Users { get; set; }
public DateTime CreatedOn { get; set; }
public DateTime UpdatedOn { get; set; }
}
}

View File

@@ -0,0 +1,44 @@
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Extensions.Options;
using Novaloop.PaymoApi.Extensions;
using Novaloop.PaymoApi.Shared;
using Novaloop.PaymoApi.Tasks.Models;
namespace Novaloop.PaymoApi.Tasks
{
public class PaymoTasksApiService : IPaymoTasksApiService
{
private readonly PaymoApiOptions _options;
private readonly HttpClient _client;
public PaymoTasksApiService(PaymoApiClient paymoApiClient, IOptions<PaymoApiOptions> options)
{
_options = options.Value;
_client = paymoApiClient.Client;
}
/// <summary>
/// Get an existing Task
/// </summary>
public async Task<GetTasksResponse> GetTask(int taskId)
{
_client.SetApiKeyHeader(_options.ApiToken);
var response = await _client.GetAsync($"/tasks/{taskId}");
await response.ThrowExceptionWithDetailsIfUnsuccessful();
return await response.Content.ReadAsAsync<GetTasksResponse>();
}
/// <summary>
/// Creates a new Task
/// </summary>
public async Task<CreateTaskResponse> CreateTask(CreateTaskRequest createTaskRequest)
{
_client.SetApiKeyHeader(_options.ApiToken);
var response = await _client.PostAsJsonAsync("/tasks", createTaskRequest);
await response.ThrowExceptionWithDetailsIfUnsuccessful();
return await response.Content.ReadAsAsync<CreateTaskResponse>();
}
}
}