Skip to content

Commit

Permalink
feat: finally working
Browse files Browse the repository at this point in the history
  • Loading branch information
rvajustin committed Aug 14, 2023
1 parent ca1a8d7 commit 8a09a91
Show file tree
Hide file tree
Showing 16 changed files with 99 additions and 36 deletions.
9 changes: 9 additions & 0 deletions src/Rosie.Nutshell/Extensions/StringExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace Rosie.Nutshell.Extensions;

public static class StringExtensions
{
public static string ToSnakeCase(this string str)
{
return string.Concat(str.Select((x, i) => i > 0 && char.IsUpper(x) ? "_" + x.ToString() : x.ToString())).ToLower();
}
}
11 changes: 11 additions & 0 deletions src/Rosie.Nutshell/Json/SnakeCaseNamingPolicy.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using System.Text.Json;
using Rosie.Nutshell.Extensions;

namespace Rosie.Nutshell.Json;

public class SnakeCaseNamingPolicy : JsonNamingPolicy
{
public static SnakeCaseNamingPolicy Instance { get; } = new();

public override string ConvertName(string name) => name.ToSnakeCase();
}
47 changes: 34 additions & 13 deletions src/Rosie.Nutshell/NutshellGateway.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text;
using System.Text.Json;
using Microsoft.Extensions.Logging;
using Rosie.Nutshell.Exceptions;
using Rosie.Nutshell.Extensions;
using Rosie.Nutshell.Json;
using Rosie.Nutshell.Secrets;
using Rosie.Nutshell.Types.Common;
using Rosie.Nutshell.Types.Endpoint;
Expand All @@ -18,6 +19,18 @@ internal class NutshellGateway : INutshellGateway
private readonly ILogger<NutshellGateway> _logger;
private readonly AsyncLazy<HttpClient> _client;
private static readonly Uri _endpointDiscoverUrl = new(Constants.DiscoveryEndpointUri);
private static Uri? _endpointUrl;

private static readonly JsonSerializerOptions _outputSerializerOptions = new()
{
PropertyNamingPolicy = SnakeCaseNamingPolicy.Instance,
PropertyNameCaseInsensitive = true
};

private static readonly JsonSerializerOptions _inputSerializationOptions = new()
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};

static NutshellGateway() => DotEnv.Load(".env");

Expand Down Expand Up @@ -56,6 +69,11 @@ private async Task<Uri> GetApiEndpointForUserAsync(string username)
{
try
{
if (_endpointUrl is not null)
{
return _endpointUrl;
}

var request = new GetEndpointRequest(username);
using var httpClient = _httpClientFactory.CreateClient(nameof(NutshellGateway));

Expand All @@ -65,7 +83,8 @@ private async Task<Uri> GetApiEndpointForUserAsync(string username)
httpClient,
_endpointDiscoverUrl);

return new Uri($"https://{endpoint.Api}/api/v1/json");
_endpointUrl = new Uri($"https://{endpoint.Api}/api/v1/json");
return _endpointUrl;
}
catch(Exception ex)
{
Expand Down Expand Up @@ -116,31 +135,31 @@ private async Task<TOut> GetResponseAsync<TOut, TIn>(
var json = await response.Content.ReadAsStringAsync();
_logger.LogInformation("Response content from Nutshell: {Response}", json);

if (!response.IsSuccessStatusCode)
{
throw new NutshellApiException($"Error while calling method `{method}`: {response.ReasonPhrase}");
}

var decoded = JsonSerializer.Deserialize<RpcResponse<TOut>>(json);
var decoded = JsonSerializer.Deserialize<RpcResponse<TOut>>(json, _outputSerializerOptions);

if (decoded?.Error != null)
{
throw new NutshellApiException(
$"Error while calling method `{method}`: {decoded.Error.Message}",
decoded.Error.Message,
decoded.Error.Code,
decoded.Error.Data);
}

if (!response.IsSuccessStatusCode)
{
throw new NutshellApiException($"Error while calling method `{method.Name}`: {response.ReasonPhrase}");
}

if (decoded is null || decoded.Result is null)
{
throw new NutshellApiException("Error while calling method `{method}`: no response");
throw new NutshellApiException($"Error while calling method `{method.Name}`: no response");
}

return decoded.Result;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error while calling method `{Method}`", method);
_logger.LogError(ex, "Error while calling method `{Method}`", method.Name);
throw;
}
}
Expand All @@ -167,12 +186,14 @@ private HttpRequestMessage CreateRequest(
}

request.Method = HttpMethod.Post;
request.Content = JsonContent.Create(payload);
var json = payload.ToJson(_inputSerializationOptions);
_logger.LogInformation("Request to Nutshell: {@Json}", json);
request.Content = new StringContent(json, Encoding.UTF8, mediaType: "application/json");
return request;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error while creating request for method `{Method}`", method);
_logger.LogError(ex, "Error while creating request for method `{Method}`", method.Name);
throw;
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/Rosie.Nutshell/Procedures/NutshellRpc.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public static class Backups
public static class Common
{
public static readonly NutshellRpc<UniversalSearchResults, SearchRequest> SearchUniversal = new("searchUniversal");
public static readonly NutshellRpc<UniversalSearchResults, FindRequest<string>> SearchByEmail = new("searchByEmail");
public static readonly NutshellRpc<UniversalSearchResults, EmailAddressQuery> SearchByEmail = new("searchByEmail");
}

public static class Competitors
Expand Down Expand Up @@ -110,7 +110,7 @@ public static class Leads
public static readonly NutshellRpc<NutshellMilestone[], FindRequest> FindMilestones = new("findMilestones");
public static readonly NutshellRpc<NutshellLead, PatchLeadRequest> EditLead = new("editLead");
public static readonly NutshellRpc<NutshellLead, GetLeadRequest> GetLead = new("getLead");
public static readonly NutshellRpc<NutshellLead, PatchLeadRequest> NewLead = new("newLead");
public static readonly NutshellRpc<NutshellLeadStub, PatchLeadRequest> NewLead = new("newLead");
public static readonly NutshellRpc<bool, GetLeadRequest> DeleteLead = new("deleteLead");
}

Expand Down
5 changes: 1 addition & 4 deletions src/Rosie.Nutshell/Types/Account/NutshellAccount.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,9 @@

namespace Rosie.Nutshell.Types.Account;

public class NutshellAccount
public class NutshellAccount : NutshellAccountStub
{
public int Id { get; set; }
public string EntityType { get; set; } = "Accounts";
public string Rev { get; set; } = null!;
public string Name { get; set; } = null!;
public string Description { get; set; } = null!;
public string LegacyId { get; set; } = null!;

Expand Down
8 changes: 8 additions & 0 deletions src/Rosie.Nutshell/Types/Account/NutshellAccountStub.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace Rosie.Nutshell.Types.Account;

public class NutshellAccountStub
{
public int Id { get; set; }
public string EntityType { get; set; } = "Accounts";
public string Name { get; set; } = null!;
}
3 changes: 3 additions & 0 deletions src/Rosie.Nutshell/Types/Common/EmailAddressQuery.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
namespace Rosie.Nutshell.Types.Common;

public record EmailAddressQuery(string EmailAddressString);
2 changes: 0 additions & 2 deletions src/Rosie.Nutshell/Types/Common/FindRequest.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
using System.ComponentModel;

namespace Rosie.Nutshell.Types.Common;

public record FindRequest(
Expand Down
6 changes: 2 additions & 4 deletions src/Rosie.Nutshell/Types/Contact/NutshellContact.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,10 @@

namespace Rosie.Nutshell.Types.Contact;

public class NutshellContact
public class NutshellContact : NutshellContactStub
{
public int Id { get; init; }
public string EntityType { get; init; }
public string Rev { get; init; }
public NutshellName Name { get; init; }
public new NutshellName Name { get; init; }
public string HtmlUrl { get; init; }
public string AvatarUrl { get; init; }
public string Description { get; init; }
Expand Down
8 changes: 8 additions & 0 deletions src/Rosie.Nutshell/Types/Contact/NutshellContactStub.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace Rosie.Nutshell.Types.Contact;

public class NutshellContactStub
{
public int Id { get; init; }
public string EntityType { get; init; }
public string Name { get; init; }
}
3 changes: 2 additions & 1 deletion src/Rosie.Nutshell/Types/Contact/PatchContactRequest.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using Rosie.Nutshell.Types.Channels;
using Rosie.Nutshell.Types.Common;
using Rosie.Nutshell.Types.Internal;

Expand All @@ -11,6 +10,8 @@ public interface IPatchKey
string Name { get; }
}

public override object GetProjection() => new { contact = base.GetProjection() };

public static class Keys
{
public static readonly TypedPatchKey<string> Name = new("name");
Expand Down
4 changes: 2 additions & 2 deletions src/Rosie.Nutshell/Types/Contact/UniversalSearchResults.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ namespace Rosie.Nutshell.Types.Contact;

public class UniversalSearchResults
{
public NutshellContact[] Contacts { get; init; }
public NutshellAccount[] Accounts { get; init; }
public NutshellContactStub[] Contacts { get; init; }
public NutshellAccountStub[] Accounts { get; init; }
}
2 changes: 1 addition & 1 deletion src/Rosie.Nutshell/Types/Internal/PatchedEntity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,5 @@ public TEntity Update<TPatchKey, TValue>(TPatchKey key, TValue value)
return this is TEntity entity ? entity : throw new InvalidOperationException();
}

public object GetProjection() => Values.ToDictionary(v => v.Key, v => v.Value);
public virtual object GetProjection() => Values.ToDictionary(v => v.Key, v => v.Value);
}
5 changes: 1 addition & 4 deletions src/Rosie.Nutshell/Types/Lead/NutshellLead.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,9 @@

namespace Rosie.Nutshell.Types.Lead;

public class NutshellLead
public class NutshellLead : NutshellLeadStub
{
public int Id { get; init; }
public string EntityType { get; init; }

Check warning on line 9 in src/Rosie.Nutshell/Types/Lead/NutshellLead.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable property 'EntityType' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.
public string Rev { get; init; }
public string Name { get; init; }
public string HtmlUrl { get; init; }

Check warning on line 10 in src/Rosie.Nutshell/Types/Lead/NutshellLead.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable property 'HtmlUrl' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.
public string AvatarUrl { get; init; }

Check warning on line 11 in src/Rosie.Nutshell/Types/Lead/NutshellLead.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable property 'AvatarUrl' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.
public string[] Tags { get; init; }

Check warning on line 12 in src/Rosie.Nutshell/Types/Lead/NutshellLead.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable property 'Tags' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.
Expand Down
8 changes: 8 additions & 0 deletions src/Rosie.Nutshell/Types/Lead/NutshellLeadStub.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace Rosie.Nutshell.Types.Lead;

public class NutshellLeadStub
{
public int Id { get; set; }
public string Rev { get; set; }

Check warning on line 6 in src/Rosie.Nutshell/Types/Lead/NutshellLeadStub.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable property 'Rev' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.
public string Name { get; set; }

Check warning on line 7 in src/Rosie.Nutshell/Types/Lead/NutshellLeadStub.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable property 'Name' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.
}
10 changes: 7 additions & 3 deletions src/Rosie.Nutshell/Types/Lead/PatchLeadRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ public interface IPatchKey
{
string Name { get; }
}


public override object GetProjection() => new { lead = base.GetProjection() };

public static class Keys
{
public static readonly TypedPatchKey<NutshellEntityId> PrimaryAccount = new("primaryAccount");
Expand All @@ -30,9 +32,11 @@ public static class Keys
public static readonly TypedPatchKey<int?> MilestoneId = new("milestoneId");
public static readonly TypedPatchKey<int?> StageSetId = new("stagesetId");
}

public class TypedPatchKey<TValue> : PatchKey<PatchLeadRequest, TValue>, IPatchKey
{
public TypedPatchKey(string name) : base(name) { }
public TypedPatchKey(string name) : base(name)
{
}
}
}

0 comments on commit 8a09a91

Please sign in to comment.