Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Slingshot Project for Elvanto #82

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace LibElvanto.Attributes
{
public class ElvantoResourceAttribute : Attribute
{
public string Url { get => url; }
private readonly string url;

public string PluralName { get => pluralName; }
private readonly string pluralName;

public string SingleName { get => singleName; }
private readonly string singleName;

public List<string> Fields { get => fields?.ToList() ?? new List<string>(); }
private readonly string[]? fields;

public ElvantoResourceAttribute( string url, string pluralName, string singleName, string[]? fields = null )
{
this.url = url;
this.pluralName = pluralName;
this.singleName = singleName;
this.fields = fields;
}
}
}
138 changes: 138 additions & 0 deletions Slingshot.Elvanto/LibElvanto/Client.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
using LibElvanto.Attributes;
using LibElvanto.Contracts;

namespace LibElvanto;

public class Client
{
private readonly string authValue;
public Client( string apiKey )
{
authValue = Convert.ToBase64String( System.Text.ASCIIEncoding.ASCII.GetBytes( $"{apiKey}:x" ) );
}

public async IAsyncEnumerable<T> Get<T>( List<string>? fields = null ) where T : ElvantoContract
{
ElvantoResourceAttribute? elvantoResource = ( ElvantoResourceAttribute? ) Attribute.GetCustomAttribute( typeof( T ), typeof( ElvantoResourceAttribute ) );

if ( elvantoResource == null )
{
throw new Exception( $"Type: {typeof( T ).Name} is not decorated with ElvantoResourceAttriubte" );
}

HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue( "Basic", authValue );

var page = 1;

fields = fields ?? new List<string>();
fields.AddRange( elvantoResource.Fields );

var fieldsString = GetFieldsString( fields );

if ( typeof( T ) == typeof( Transaction ) )
{
fieldsString += $"&start=2000-01-01&end={DateTime.Today:yyyy-MM-dd}";
}

do
{
var results = await client.GetAsync( $"{elvantoResource.Url}?page={page}{fieldsString}" );
if ( results == null )
{
throw new Exception( "Bad request" );
}

var content = await results.Content.ReadAsStringAsync();

var paginatedResponse = ParseContent<T>( content, elvantoResource, fields );

foreach ( var item in paginatedResponse.Data )
{
yield return item;
}

if ( paginatedResponse.Total < page * paginatedResponse.PerPage )
{
break;
}

page++;
}
while ( true );
}

private PaginatedResponse<T> ParseContent<T>(
string content,
ElvantoResourceAttribute elvantoResource,
List<string> fields ) where T : ElvantoContract
{
var document = JsonDocument.Parse( content );

if ( document == null )
{
throw new Exception( "Could not parse JSON" );
}

var response = new PaginatedResponse<T>();

var root = document.RootElement;
root.TryGetProperty( elvantoResource.PluralName, out var plural );


if ( plural.TryGetProperty( "total", out var totalElement ) && totalElement.ValueKind == JsonValueKind.Number && totalElement.TryGetInt32( out var total ) )
{
response.Total = total;
}

if ( plural.TryGetProperty( "page", out var pageElement ) && pageElement.ValueKind == JsonValueKind.Number && pageElement.TryGetInt32( out var page ) )
{
response.Page = page;
}

if ( plural.TryGetProperty( "per_page", out var perpageElement ) && perpageElement.ValueKind == JsonValueKind.Number && perpageElement.TryGetInt32( out var perpage ) )
{
response.PerPage = perpage;
}

if ( plural.TryGetProperty( "on_this_page", out var onthisPageElement ) && onthisPageElement.ValueKind == JsonValueKind.Number && onthisPageElement.TryGetInt32( out var onthisPage ) )
{
response.OnThisPage = onthisPage;
}

if ( plural.TryGetProperty( elvantoResource.SingleName, out var dataset ) )
{
var dataElements = dataset.EnumerateArray();
foreach ( var dataElement in dataElements )
{
var item = dataElement.Deserialize<T>();

if ( item == null )
{
continue;
}

response.Data.Add( item );

item.Process( dataElement, fields );

}
}

return response;
}

private string GetFieldsString( List<string> fields )
{
StringBuilder stringBuilder = new StringBuilder();
for ( var i = 0; i < fields.Count; i++ )
{
stringBuilder.Append( $"&fields[{i}]={fields[i]}" );
}
return stringBuilder.ToString();
}

}
19 changes: 19 additions & 0 deletions Slingshot.Elvanto/LibElvanto/Contracts/Category.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using LibElvanto.Attributes;

namespace LibElvanto.Contracts;

[ElvantoResource( "https://api.elvanto.com/v1/people/categories/getAll.json", "categories", "category" )]
public class Category : ElvantoContract
{
[JsonPropertyName("id")]
public string? Id { get; set; }

[JsonPropertyName("name")]
public string? Name { get; set; }
}
41 changes: 41 additions & 0 deletions Slingshot.Elvanto/LibElvanto/Contracts/CustomFields.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using LibElvanto.Attributes;

namespace LibElvanto.Contracts;

[ElvantoResource( "https://api.elvanto.com/v1/people/customFields/getAll.json", "custom_fields", "custom_field" )]
public class CustomFields : ElvantoContract
{
[JsonPropertyName( "id" )]
public string? Id { get; set; }

[JsonPropertyName( "name" )]
public string? Name { get; set; }

[JsonPropertyName( "type" )]
public string? Type { get; set; }

[JsonPropertyName( "values" )]
public dynamic? Values { get; set; }
}

public class Value
{
[JsonPropertyName( "id" )]
public string? Id { get; set; }

[JsonPropertyName( "name" )]
public string? Name { get; set; }
}

public class Values
{
[JsonPropertyName( "value" )]
public List<Value>? Value { get; set; }
}

19 changes: 19 additions & 0 deletions Slingshot.Elvanto/LibElvanto/Contracts/ElvantoContract.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;

namespace LibElvanto.Contracts;

public abstract class ElvantoContract
{
public Dictionary<string, string> AttributeValues { get; set; } = new Dictionary<string, string>();

public virtual void Process( JsonElement dataElement, List<string>? fields )
{

}

}
20 changes: 20 additions & 0 deletions Slingshot.Elvanto/LibElvanto/Contracts/FinancialCategory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using LibElvanto.Attributes;

namespace LibElvanto.Contracts;

[ElvantoResource( "https://api.elvanto.com/v1/financial/categories/getAll.json", "categories", "category" )]
public class FinancialCategory : ElvantoContract
{
[JsonPropertyName( "id" )]
public string? Id { get; set; }

[JsonPropertyName( "name" )]
public string? Name { get; set; }

}
Loading